From 082bf4562e92294d23072be48e629a4ceea4dbf8 Mon Sep 17 00:00:00 2001 From: Evoltsuki <67886200+Evoltsuki@users.noreply.github.com> Date: Thu, 30 Apr 2026 17:55:34 +0800 Subject: [PATCH 1/9] =?UTF-8?q?feat(multi=5Finstance):=20=E5=A2=9E?= =?UTF-8?q?=E5=BC=BA=E5=A4=9A=E5=BC=80=E7=AE=A1=E7=90=86=E5=99=A8=E7=9A=84?= =?UTF-8?q?=E7=A8=B3=E5=AE=9A=E6=80=A7=E5=92=8C=E7=94=A8=E6=88=B7=E4=BD=93?= =?UTF-8?q?=E9=AA=8C=20(#142)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加智能端口输入框,支持延迟格式化 - 实现崩溃检测和自动恢复机制,监控实例活动状态 - 优化设备连接逻辑,只使用指定设备不自动选择 - 调整超时检测机制,统一处理所有状态的超时情况 - 改进登录流程,增加动态检测和弹窗处理 - 添加心跳机制保持实例活跃状态 - 保存实例配置便于崩溃后恢复 --- auto_fetch.py | 20 +++--- loadData.py | 9 +-- login.py | 83 ++++++++++++++--------- multi_instance.py | 165 +++++++++++++++++++++++++++++++++++++++------- 4 files changed, 207 insertions(+), 70 deletions(-) diff --git a/auto_fetch.py b/auto_fetch.py index a90fa93..0a5909c 100644 --- a/auto_fetch.py +++ b/auto_fetch.py @@ -580,18 +580,17 @@ def auto_fetch_data(self): self.state_start_time = time.time() # 重置状态开始时间 - # UNKNOWN 状态超时检测 - # 非战斗状态:超过 36 秒触发重启(登录过程、主菜单、模式选择等) - # 战斗流程状态:超过 200 秒触发重启(防止战斗卡死) + # 全局超时检测:无论什么状态,只要超过时间都可能触发重启 + # 非战斗状态:超过 50 秒触发重启 + # 战斗流程状态:超过 120 秒触发重启(防止战斗卡死) elapsed_time = time.time() - self.state_start_time is_battle_state = self.last_state in [GameState.PRE_BATTLE, GameState.IN_BATTLE, GameState.SETTLEMENT, GameState.FINISHED] - timeout_threshold = 200.0 if is_battle_state else 36.0 + timeout_threshold = 120.0 if is_battle_state else 50.0 - if current_state == GameState.UNKNOWN and elapsed_time > timeout_threshold: - if is_battle_state: - self._log(logging.WARNING, f"战斗流程中连续 {elapsed_time:.2f} 秒处于未知状态,超过200秒阈值,触发重启") - else: - self._log(logging.WARNING, f"连续 {elapsed_time:.2f} 秒处于未知状态,超过36秒阈值,触发重启") + # 检查是否超时 + if elapsed_time > timeout_threshold: + state_name = self.last_state.name if self.last_state else "NONE" + self._log(logging.WARNING, f"在状态 '{state_name}' 停留超过 {elapsed_time:.2f} 秒(阈值: {timeout_threshold:.0f} 秒),触发重启") # 使用 LoginManager 的重启登录方法 if not self.login_manager.can_restart(): @@ -692,6 +691,9 @@ def auto_fetch_loop(self): if not self.auto_fetch_running: break + # 更新活动时间(心跳) + self.update_prediction_callback(0) + self.auto_fetch_data() # 每次循环结束时检查状态 diff --git a/loadData.py b/loadData.py index 1385a0f..5481459 100644 --- a/loadData.py +++ b/loadData.py @@ -133,13 +133,8 @@ def update_device_serial(self, serial): self.device_serial = serial return dev - # 自动选择第一个可用设备 - if devices: - self.device_serial = devices[0] - logger.info(f"自动选择第一个可用设备: {self.device_serial}") - return self.device_serial - - logger.error("未找到可连接的Android设备") + # 只使用指定的设备,不要自动选择其他设备 + logger.error(f"未找到指定的设备: {serial},当前在线设备: {devices}") self.device_serial = "" return "" diff --git a/login.py b/login.py index 2cb88e4..fea67ae 100644 --- a/login.py +++ b/login.py @@ -345,16 +345,32 @@ def sleep_with_check(seconds): return False # 等待登录完成,最多等待70秒 - self._log(logging.INFO, "等待登录完成") + self._log(logging.INFO, "等待登录完成 (最长70秒,动态检测)...") start_time = time.time() + while time.time() - start_time < 70: if not check_stop(): return False - # 简单的等待,不进行复杂的页面检测 - if not sleep_with_check(1): + + # 每隔3秒进行一次检测,避免频繁截图和识别抢占CPU(特别是多开时) + if not sleep_with_check(3): return False + + screenshot = self.connector.capture_screenshot() + if screenshot is not None: + comp_matched, _ = self.match_template(screenshot, "competition_page", threshold=0.7) + ann_matched, _ = self.match_template(screenshot, "announcement_close", threshold=0.9) + event_matched, _ = self.match_template(screenshot, "event_claim_close", threshold=0.9) + + if comp_matched or ann_matched or event_matched: + elapsed = int(time.time() - start_time) + self._log(logging.INFO, f"检测到游戏界面已加载 (耗时: {elapsed}秒),提前结束等待") + if not sleep_with_check(3): + return False + + break - # 寻找争锋频道页面,最多等待30秒 + # 寻找争锋频道入口,最多等待30秒 self._log(logging.INFO, "寻找争锋频道入口") start_time = time.time() @@ -365,50 +381,55 @@ def sleep_with_check(seconds): return True # 识别失败时点击右上角两次 - click_count = 0 - while click_count < 2: - self._log(logging.INFO, "未检测到争锋频道入口,点击屏幕右上角") + self._log(logging.INFO, "未检测到争锋频道入口,点击屏幕右上角") + for _ in range(2): self.connector.click((0.1, 0.1)) if not sleep_with_check(2): return False - click_count += 1 # 点击右上角后,立即再次检查争锋频道入口 - if not sleep_with_check(1): - return False screenshot = self.connector.capture_screenshot() if screenshot is not None: if self._check_and_click_competition_page(screenshot, sleep_with_check, check_stop): return True - - # 识别失败后识别关闭按钮 + + # 同时检测两种关闭按钮五次(每次间隔2秒) + self._log(logging.INFO, "尝试检测并关闭公告/活动弹窗") close_buttons = ["announcement_close", "event_claim_close"] - close_button_found = False - for button in close_buttons: + for i in range(5): screenshot = self.connector.capture_screenshot() if screenshot is not None: - matched, pos = self.match_template(screenshot, button, threshold=0.9) - if matched: - h, w = screenshot.shape[:2] - rel_x = pos[0] / w - rel_y = pos[1] / h - self._log(logging.INFO, f"{button}位置: ({pos[0]}, {pos[1]}), 相对坐标: ({rel_x:.2f}, {rel_y:.2f})") - self.connector.click((rel_x, rel_y)) - self._log(logging.INFO, f"关闭{button}页面") - close_button_found = True - if not sleep_with_check(1): - return False - break - - # 再次识别争锋频道入口 - if close_button_found: - if not sleep_with_check(1): + for button in close_buttons: + matched, pos = self.match_template(screenshot, button, threshold=0.9) + if matched: + h, w = screenshot.shape[:2] + rel_x = pos[0] / w + rel_y = pos[1] / h + self._log(logging.INFO, f"{button}位置: ({pos[0]}, {pos[1]}), 相对坐标: ({rel_x:.2f}, {rel_y:.2f})") + self.connector.click((rel_x, rel_y)) + self._log(logging.INFO, f"第 {i+1} 次检测,关闭 {button} 页面") + break # 发现并点击了一个按钮后,跳出内层循环,避免同一张截图重复点击 + + # 每次检测完等待2秒 + if not sleep_with_check(2): return False + + # 关闭完之后,20秒内重复检测争锋频道入口 + self._log(logging.INFO, "检测争锋频道入口") + check_start_time = time.time() + while time.time() - check_start_time < 20: + if not check_stop(): + return False + screenshot = self.connector.capture_screenshot() if screenshot is not None: if self._check_and_click_competition_page(screenshot, sleep_with_check, check_stop): return True - + + # 每次轮询间隔2秒,避免频繁截图造成性能浪费 + if not sleep_with_check(2): + return False + # 还是失败的话就重启 self._log(logging.ERROR, "未找到争锋频道入口,登录流程失败") return False diff --git a/multi_instance.py b/multi_instance.py index aae0b91..b9307c2 100644 --- a/multi_instance.py +++ b/multi_instance.py @@ -9,7 +9,7 @@ QPushButton, QLabel, QPlainTextEdit, QSpinBox, QComboBox, QCheckBox, QMessageBox, QSplitter, QScrollArea, QFrame, QLineEdit ) -from PyQt6.QtCore import Qt, QTimer, pyqtSignal, QObject +from PyQt6.QtCore import Qt, QTimer, pyqtSignal, QObject, pyqtSlot from PyQt6.QtGui import QFont import loadData @@ -57,6 +57,46 @@ def append_log(self, text): logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) + +class SmartPortsLineEdit(QLineEdit): + """智能端口输入框:支持延迟格式化、失去焦点格式化和粘贴立即格式化""" + def __init__(self, parent=None): + super().__init__(parent) + self.format_timer = QTimer(self) + self.format_timer.setSingleShot(True) + self.format_timer.timeout.connect(self._format_text) + self._formatting = False + + def keyPressEvent(self, event): + super().keyPressEvent(event) + if not self._formatting: + self.format_timer.start(800) # 800ms 延迟 + + def focusOutEvent(self, event): + super().focusOutEvent(event) + self._format_text() + + def paste(self): + super().paste() + self.format_timer.start(100) # 粘贴后快速格式化 + + def _format_text(self): + if self._formatting: + return + self._formatting = True + + text = self.text() + parts = text.replace('\n', ',').replace(',', ',').replace(';', ',').replace(' ', ',').split(',') + ports = [p.strip() for p in parts if p.strip().isdigit()] + formatted = ", ".join(ports) + + if text != formatted and ports: + cursor_pos = self.cursorPosition() + self.setText(formatted) + self.setCursorPosition(min(cursor_pos, len(formatted))) + + self._formatting = False + # 用于共享资源密集型对象,减少多开时的内存占用 _cannot_model = None _recognizer = None @@ -122,13 +162,25 @@ def __init__(self, port): self.auto_fetch_thread = None # 保存线程引用 self.stop_event = threading.Event() # 使用Event对象进行线程间通信,更可靠 self.start_time = None # 实例启动时间戳 + self.last_activity_time = time.time() # 最后活动时间戳(用于检测崩溃) + self.thread_running = False # 线程是否正在运行的标志 + self.game_mode = None # 保存游戏模式 + self.is_invest = None # 保存投资设置 def start(self, game_mode, is_invest): try: - logger.info(f"[{self.serial}] 开始启动实例,游戏模式: {game_mode}, 自动投资: {is_invest}") + # 保存设置 + self.game_mode = game_mode + self.is_invest = is_invest + + # 重置停止事件和标志 self.stop_event.clear() + self.thread_running = True + self.last_activity_time = time.time() self.status = "连接中" + logger.info(f"[{self.serial}] 开始启动实例,游戏模式: {game_mode}, 自动投资: {is_invest}") + # 记录实例启动时间戳 self.start_time = time.time() @@ -136,6 +188,7 @@ def start(self, game_mode, is_invest): if not self.connector.is_connected: self.status = "连接失败" logger.error(f"[{self.serial}] 连接失败") + self.thread_running = False return False self.login_manager = LoginManager(self.connector) @@ -147,6 +200,7 @@ def start(self, game_mode, is_invest): if self.stop_event.is_set(): logger.info(f"[{self.serial}] 登录过程被用户停止") self.status = "已停止" + self.thread_running = False return False if login_success: @@ -158,17 +212,18 @@ def start(self, game_mode, is_invest): if self.stop_event.is_set(): logger.info(f"[{self.serial}] 用户在登录成功后点击了停止") self.status = "已停止" + self.thread_running = False return False self.auto_fetch = auto_fetch.AutoFetch( self.connector, game_mode, is_invest, - update_prediction_callback=lambda x: None, - update_monster_callback=lambda x: None, - updater=lambda: None, + update_prediction_callback=self._update_activity_time, + update_monster_callback=self._update_activity_time, + updater=self._update_activity_time, start_callback=lambda: None, - stop_callback=lambda: None, + stop_callback=self._on_stop_callback, training_duration=-1, recognizer=get_recognizer(), cannot_model=get_cannot_model(), @@ -183,12 +238,23 @@ def start(self, game_mode, is_invest): except Exception as e: self.status = f"错误: {str(e)}" logger.error(f"[{self.serial}] 启动失败: {str(e)}") + self.thread_running = False return False + + def _update_activity_time(self, *args, **kwargs): + """更新活动时间""" + self.last_activity_time = time.time() + + def _on_stop_callback(self): + """当 auto_fetch 停止时的回调""" + self.thread_running = False + logger.info(f"[{self.serial}] auto_fetch 已停止") def stop(self): logger.info(f"[{self.serial}] 强制停止实例") - # 设置停止事件,用于中断登录过程 + # 设置停止事件,用于中断登录流程 self.stop_event.set() + self.thread_running = False if self.auto_fetch: # 强制设置停止标志,不等待线程退出 @@ -240,6 +306,11 @@ def __init__(self): self.timer = QTimer() self.timer.timeout.connect(self.update_display) self.timer.start(1000) + + # 崩溃检测定时器(每 5 秒检查一次) + self.crash_detection_timer = QTimer() + self.crash_detection_timer.timeout.connect(self.check_instances_crash) + self.crash_detection_timer.start(5000) def init_ui(self): central_widget = QWidget() @@ -264,7 +335,7 @@ def init_ui(self): # 端口输入(横向,逗号分隔) ports_layout = QHBoxLayout() ports_layout.addWidget(QLabel("端口:")) - self.ports_input = QLineEdit() + self.ports_input = SmartPortsLineEdit() self.ports_input.setPlaceholderText("16416, 16448, 16480, 16512") try: if Path("multi_ports.txt").exists(): @@ -273,7 +344,6 @@ def init_ui(self): self.ports_input.setText(", ".join(ports)) except: pass - self.ports_input.textChanged.connect(self._on_ports_text_changed) ports_layout.addWidget(self.ports_input) layout.addLayout(ports_layout) @@ -330,26 +400,12 @@ def init_ui(self): layout.addWidget(splitter) self.port_widgets = {} - self._formatting_ports = False @staticmethod def _parse_ports(text): parts = text.replace('\n', ',').replace(',', ',').replace(';', ',').replace(' ', ',').split(',') return [p.strip() for p in parts if p.strip().isdigit()] - def _on_ports_text_changed(self): - if self._formatting_ports: - return - text = self.ports_input.text() - ports = self._parse_ports(text) - formatted = ", ".join(ports) - if text != formatted and ports: - cursor_pos = self.ports_input.cursorPosition() - self._formatting_ports = True - self.ports_input.setText(formatted) - self.ports_input.setCursorPosition(min(cursor_pos, len(formatted))) - self._formatting_ports = False - def start_all(self): try: Path("multi_ports.txt").write_text(self.ports_input.text()) @@ -552,6 +608,69 @@ def do_start(): threading.Thread(target=do_start, daemon=True).start() + def check_instances_crash(self): + """检查实例是否崩溃,并尝试自动恢复""" + current_time = time.time() + + # 遍历所有实例,检查崩溃情况 + for port, instance in list(self.instances.items()): + # 如果已经设置了停止事件,就不进行崩溃检测 + if instance.stop_event.is_set(): + continue + + # 检查活动时间(崩溃检测) + inactive_time = current_time - instance.last_activity_time + + # 如果标记为运行但超过 3 分钟没有活动,视为崩溃 + if instance.status in ["正在运行", "连接中", "登录中"] and inactive_time > 180: + logger.warning(f"[{instance.serial}] 检测到无活动超过 {inactive_time:.0f} 秒,可能已崩溃,尝试重启") + self._restart_crashed_instance(port, instance) + # 检查 auto_fetch 线程是否实际还在运行 + elif instance.status == "正在运行" and instance.auto_fetch: + if not instance.auto_fetch.auto_fetch_running: + logger.warning(f"[{instance.serial}] auto_fetch_running 为 False,可能已意外停止") + self._restart_crashed_instance(port, instance) + + def _restart_crashed_instance(self, port, instance): + """重启崩溃的实例""" + try: + # 保存设置 + game_mode = instance.game_mode if instance.game_mode else "30人" + is_invest = instance.is_invest if instance.is_invest is not None else False + + # 先停止(强制) + instance.stop() + + # 从字典中清除 + if port in self.instances: + del self.instances[port] + + # 延迟重新启动 + logger.info(f"[{instance.serial}] 准备重新启动...") + + def restart_task(): + if port in self.starting_ports: + logger.warning(f"端口 {port} 已在启动中,跳过重启") + return + + logger.info(f"[{instance.serial}] 正在重新启动...") + self.starting_ports.add(port) + + try: + new_instance = DeviceInstance(port) + self.instances[port] = new_instance + success = new_instance.start(game_mode, is_invest) + if success: + logger.info(f"[{instance.serial}] 崩溃后重启成功") + else: + logger.error(f"[{instance.serial}] 崩溃后重启失败") + finally: + self.starting_ports.discard(port) + + threading.Thread(target=restart_task, daemon=True).start() + except Exception as e: + logger.error(f"重启崩溃实例时出错: {str(e)}") + def update_display(self): input_ports = self._parse_ports(self.ports_input.text()) From 067ea145f515a29cd1fa2bd856c27ca7af65094f Mon Sep 17 00:00:00 2001 From: 1ZUMIKun <68433151+1ZUMIKun@users.noreply.github.com> Date: Thu, 30 Apr 2026 21:51:09 +0800 Subject: [PATCH 2/9] Remove incorrect update and add sleep time --- auto_fetch.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/auto_fetch.py b/auto_fetch.py index 0a5909c..7a6bbf9 100644 --- a/auto_fetch.py +++ b/auto_fetch.py @@ -691,9 +691,6 @@ def auto_fetch_loop(self): if not self.auto_fetch_running: break - # 更新活动时间(心跳) - self.update_prediction_callback(0) - self.auto_fetch_data() # 每次循环结束时检查状态 @@ -705,7 +702,7 @@ def auto_fetch_loop(self): self._log(logging.INFO, "已达到设定时长,结束自动获取") break # 检测一次间隔时间—————————————————————————————————— - time.sleep(0.1) + time.sleep(0.2) except Exception as e: self._log(logging.ERROR, f"自动获取数据出错:\n{e}") break From eea5efbba270311c453a05806b197fa2c0ab74c2 Mon Sep 17 00:00:00 2001 From: 1ZUMIKun <68433151+1ZUMIKun@users.noreply.github.com> Date: Thu, 30 Apr 2026 22:06:01 +0800 Subject: [PATCH 3/9] Fix multi instance force stop autofetch Co-authored-by: Copilot --- auto_fetch.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/auto_fetch.py b/auto_fetch.py index 7a6bbf9..a17cf02 100644 --- a/auto_fetch.py +++ b/auto_fetch.py @@ -690,9 +690,10 @@ def auto_fetch_loop(self): # 每次循环开始时检查状态 if not self.auto_fetch_running: break - + + self.updater() # 多开器如果不更新会强制停止 self.auto_fetch_data() - + # 每次循环结束时检查状态 if not self.auto_fetch_running: break From 8c1a922659ba5d3602d825a44add22e6aec4c7a1 Mon Sep 17 00:00:00 2001 From: AbyssRei Date: Fri, 1 May 2026 05:49:29 +0800 Subject: [PATCH 4/9] Refactor project structure and enhance launcher script functionality (#151) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: 规范项目目录结构与模块分层,提升可维护性以符合标准工程化要求 * fix: 补全文件 Co-authored-by: Copilot * Refactor UV installation and inference environment selection logic #149 This pull request introduces improvements to both the user experience and code logic in the launcher batch script and the auto-fetch module. The batch script now provides more flexible inference environment options for users, while the auto-fetch logic is refined to prevent unintentional overwriting of prediction results. Launcher script enhancements: Expanded the inference environment selection in iron_shark_launcher.bat to allow users to choose between PyTorch CPU, CUDA 12.8, CUDA 13.0, or onnxruntime, with improved prompt text and logic for handling user input. Updated the script to use uv sync with the appropriate --extra flag based on the user's choice, rather than always attempting to install PyTorch, and clarified messages to the user. Auto-fetch logic improvement: In src/game/auto_fetch.py, changed the heartbeat update to use the current prediction value instead of a fixed 0, preventing accidental overwriting of GUI prediction results. * Refactor UV installation logic and add path management module (#150) * refactor: 优化uv安装和推理环境选择逻辑 Co-authored-by: Copilot * fix(auto_fetch): 刷新当前预测显示,避免覆盖 GUI 错误 * fix(sim): 修复路径问题 * feat(paths): 添加路径管理模块,统一资源路径管理 - 新增 paths.py 文件,定义项目根目录、资源目录、数据目录等路径 - 更新多个模块以使用新的路径管理函数,确保路径的一致性和可维护性 Co-authored-by: Copilot --------- Co-authored-by: Copilot --------- Co-authored-by: Copilot --- .github/workflows/build-and-release.yml | 4 +- .gitignore | 19 +- WinningRate_Statistics.py | 942 --------- ...7\211\210).bat" => iron_shark_launcher.bat | 152 +- main.py | 383 ++-- main.spec | 14 +- main_old.py | 650 ------ main_sim.py | 569 ++++-- models/update.md | 7 - multi_instance.py | 432 ++-- pyproject.toml | 4 +- simulator/.gitignore | 1 - simulator/scene.json | 58 - simulator/stats.py | 172 -- specialmonster.py | 37 - {models => src}/__init__.py | 0 {simulator => src/analysis}/__init__.py | 0 sim_mc.py => src/analysis/sim_mc.py | 330 ++- .../analysis/similar_history_match.py | 210 +- src/analysis/winning_rate_statistics.py | 1131 +++++++++++ .../core/__init__.py | 0 config.py => src/core/config.py | 31 +- constants.py => src/core/constants.py | 58 +- src/core/paths.py | 38 + src/data/__init__.py | 0 data_package.py => src/data/data_package.py | 4 +- loadData.py => src/data/load_data.py | 220 +- src/game/__init__.py | 0 auto_fetch.py => src/game/auto_fetch.py | 427 ++-- login.py => src/game/login.py | 318 ++- .../game/maa_adb_connector.py | 131 +- winrt_capture.py => src/game/winrt_capture.py | 60 +- src/models/__init__.py | 0 {models => src/models}/model.py | 14 +- {models => src/models}/muon.py | 55 +- predict.py => src/models/predict.py | 189 +- predict_onnx.py => src/models/predict_onnx.py | 91 +- train.py => src/models/train.py | 264 ++- val.py => src/models/val.py | 55 +- src/recognition/__init__.py | 0 .../recognition/field_recognition.py | 137 +- .../recognition/find_monster_zone.py | 367 +++- recognize.py => src/recognition/recognize.py | 146 +- src/recognition/specialmonster.py | 44 + .../resources/assets/icons}/background.png | Bin {ico => src/resources/assets/icons}/icon.ico | Bin .../resources/assets/icons}/icon_64x64.ico | Bin ...0\345\212\233\350\243\205\347\224\262.png" | Bin ...0\345\212\233\350\243\205\347\224\262.png" | Bin .../resources/assets/images}/eg.png | Bin .../resources/assets/images}/empty.png | Bin .../images}/login/announcement_close.png | Bin .../assets/images}/login/competition_page.png | Bin .../images}/login/event_claim_close.png | Bin .../assets/images}/login/login_button.png | Bin .../assets/images}/login/login_off.png | Bin .../resources/assets/images}/process/0.png | Bin .../resources/assets/images}/process/1.png | Bin .../resources/assets/images}/process/10.png | Bin .../resources/assets/images}/process/11.png | Bin .../resources/assets/images}/process/12.png | Bin .../resources/assets/images}/process/13.png | Bin .../resources/assets/images}/process/14.png | Bin .../resources/assets/images}/process/15.png | Bin .../images}/process/1746774840_28_5.png1s.png | Bin .../resources/assets/images}/process/2.png | Bin .../resources/assets/images}/process/3.png | Bin .../resources/assets/images}/process/4.png | Bin .../resources/assets/images}/process/5.png | Bin .../resources/assets/images}/process/6.png | Bin .../resources/assets/images}/process/7.png | Bin .../resources/assets/images}/process/8.png | Bin .../resources/assets/images}/process/9.png | Bin ...5\344\273\207\350\200\205\342\200\235.png" | Bin .../\342\200\234\345\246\222\342\200\235.png" | Bin ...4\345\267\242\347\251\264\342\200\235.png" | Bin ...4\345\272\236\350\264\235\342\200\235.png" | Bin ...5\347\237\263\346\234\272\342\200\235.png" | Bin ...4\347\233\233\346\200\222\342\200\235.png" | Bin ...4\350\207\252\345\234\250\342\200\235.png" | Bin ...4\346\204\217\345\277\227\342\200\235.png" | Bin ...3\347\224\237\351\243\216\342\200\235.png" | Bin .../\342\200\234\351\227\250\342\200\235.png" | Bin ...4\351\230\277\345\222\254\342\200\235.png" | Bin ...1\350\242\255\345\274\251\346\211\213.png" | Bin ...5\346\262\231\346\273\251\350\275\246.png" | Bin ...7\345\215\253\346\263\225\350\200\205.png" | Bin ...0\345\216\237\346\234\257\345\270\210.png" | Bin ...6\346\272\220\347\237\263\350\231\253.png" | Bin ...3\351\233\266\351\252\221\345\243\253.png" | Bin .../\345\207\266\350\261\225\345\205\275.png" | Bin ...5\347\224\262\346\255\245\345\205\265.png" | Bin ...2\345\267\250\345\262\251\350\233\233.png" | Bin ...3\345\233\242\345\255\246\345\276\222.png" | Bin ...7\345\207\277\347\237\263\350\200\205.png" | Bin .../\345\244\215\344\273\207\350\200\205.png" | Bin ...0\345\237\265\346\235\203\345\214\226.png" | Bin ...3\346\265\201\346\265\252\350\200\205.png" | Bin ...7\350\200\205\344\277\241\344\275\277.png" | Bin ...7\347\252\245\351\255\205\344\272\272.png" | Bin .../images/\345\262\201\347\233\270.png" | Bin .../\345\262\251\345\206\240\345\205\275.png" | Bin ...1\351\224\213\345\215\253\351\225\277.png" | Bin ...3\346\211\213\345\233\232\347\212\257.png" | Bin ...4\345\245\275\346\210\230\350\200\205.png" | Bin ...4\347\240\264\345\235\217\347\216\213.png" | Bin ...3\345\233\242\345\255\246\345\276\222.png" | Bin .../images/\346\227\272\350\264\242.png" | Bin ...0\351\243\237\344\272\272\350\212\261.png" | Bin ...0\351\243\237\350\231\253\350\212\261.png" | Bin ...2\346\216\250\350\215\220\350\200\205.png" | Bin ...1\345\273\211\345\247\206\346\226\257.png" | Bin ...0\346\261\275\346\210\230\350\275\246.png" | Bin .../\346\236\243\345\244\247\345\210\200.png" | Bin ...7\346\234\275\344\271\213\347\247\215.png" | Bin .../images/\346\243\230\345\205\275.png" | Bin ...0\345\233\242\351\274\223\346\211\213.png" | Bin ...0\345\205\213\346\226\257\346\211\213.png" | Bin ...4\346\211\213\351\207\215\350\211\207.png" | Bin ...4\351\201\201\345\277\215\350\200\205.png" | Bin .../images/\346\262\211\346\262\231.png" | Bin ...3\345\233\242\347\262\276\351\224\220.png" | Bin ...7\350\267\265\350\241\214\350\200\205.png" | Bin ...5\345\262\251\345\267\250\345\203\217.png" | Bin ...7\350\243\202\347\244\201\350\200\205.png" | Bin ...6\350\243\205\351\251\256\345\205\275.png" | Bin ...3\351\230\237\347\233\276\345\215\253.png" | Bin ...4\345\277\227\346\204\277\350\200\205.png" | Bin ...3\347\225\270\345\217\230\344\275\223.png" | Bin ...5\346\272\220\347\237\263\350\231\253.png" | Bin .../\347\202\255\351\225\277\347\237\233.png" | Bin ...6\345\207\273\347\273\204\351\225\277.png" | Bin ...0\346\272\220\347\237\263\350\231\253.png" | Bin ...7\344\270\273\347\273\204\351\225\277.png" | Bin ...47\232\204\347\214\216\347\213\227pro.png" | Bin ...1\345\207\273\346\255\245\345\205\265.png" | Bin .../\347\216\211\345\217\214\345\211\221.png" | Bin .../images/\347\224\260\351\274\267.png" | Bin ...0\350\265\230\347\224\237\347\211\251.png" | Bin .../resources/assets/images/\347\230\264.png" | Bin ...7\350\204\211\345\256\210\345\215\253.png" | Bin ...1\345\244\264\346\260\264\346\211\213.png" | Bin ...4\347\224\262\347\210\252\345\205\275.png" | Bin .../\347\251\272\351\231\215\345\205\265.png" | Bin ...0\346\233\262\345\220\210\345\243\260.png" | Bin ...0\350\264\245\351\252\221\345\243\253.png" | Bin ...3\345\220\211\344\273\226\342\200\235.png" | Bin ...4\350\264\235\346\226\257\342\200\235.png" | Bin ...7\345\212\253\346\216\240\350\200\205.png" | Bin ...1\345\244\247\345\211\221\346\211\213.png" | Bin ...2\347\231\276\345\244\253\351\225\277.png" | Bin ...5\345\220\236\345\231\254\350\200\205.png" | Bin ...7\346\234\275\345\256\277\345\215\253.png" | Bin ...7\346\234\275\346\210\230\350\275\246.png" | Bin ...0\351\233\206\345\205\273\350\200\205.png" | Bin ...6\344\272\272\345\274\251\346\211\213.png" | Bin ...2\346\211\260\344\271\261\350\200\205.png" | Bin ...7\347\232\204\345\267\250\345\203\217.png" | Bin ...7\237\263\350\231\253\302\267\316\261.png" | Bin ...5\351\230\262\345\276\241\350\200\205.png" | Bin ...0\345\207\273\344\272\272\346\211\213.png" | Bin ...7\346\234\257\345\270\210\342\200\235.png" | Bin ...0\345\241\224\346\234\257\345\270\210.png" | Bin ...0\346\231\256\345\260\274\345\205\213.png" | Bin ...6\350\243\205\344\272\272\345\221\230.png" | Bin ...5\346\272\220\347\237\263\350\231\253.png" | Bin ...4\346\272\220\347\237\263\350\231\253.png" | Bin src/resources/config/maa_option.json | 7 + monster.csv => src/resources/data/monster.csv | 0 .../resources/data/monster_greenvine.csv | 0 {simulator => src/simulation}/README.md | 0 src/simulation/__init__.py | 0 {simulator => src/simulation}/arknights.csv | 0 {simulator => src/simulation}/battle_field.py | 154 +- {simulator => src/simulation}/elemental.py | 50 +- {simulator => src/simulation}/monsters.json | 0 {simulator => src/simulation}/monsters.py | 623 ++++-- {simulator => src/simulation}/projectiles.py | 138 +- src/simulation/scene.json | 206 ++ {simulator => src/simulation}/simulate.py | 74 +- src/simulation/stats.py | 191 ++ {simulator => src/simulation}/utils.py | 57 +- {simulator => src/simulation}/vector2d.py | 57 +- {simulator => src/simulation}/zone.py | 57 +- {tools => src/tools}/HumanDataCheck.py | 240 ++- .../tools}/battlefield_composite/.gitignore | 0 .../battlefield_composite.py | 41 +- .../extract_webm_frames.py | 93 + .../monster_images/IM-1.png | Bin .../monster_images/battlefield_empty.jpg | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...4-\346\210\230\346\226\227-A_Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...4-\346\210\230\346\226\227-Move_A-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...4-\346\210\230\346\226\227-A_Move-x1.webm" | Bin ...\346\210\230\346\226\227-Move_red-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...-\346\210\230\346\226\227-C1_Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...4-\346\210\230\346\226\227-Attack-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...346\210\230\346\226\227-Move_Loop-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...346\210\230\346\226\227-Move_Loop-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...346\210\230\346\226\227-Move_Loop-x1.webm" | Bin ...346\210\230\346\226\227-Move_Loop-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin .../battlefield_recognize/class_to_idx.json | 0 ...7\346\256\265\350\257\264\346\230\216.txt" | 0 {tools => src/tools}/convert_model.py | 20 +- {tools => src/tools}/data_cleaning.py | 6 +- .../data_cleaning_with_field_recognize.py | 191 +- .../data_cleaning_with_field_recognize_gpu.py | 208 +- {tools => src/tools}/data_nanWriter.py | 7 +- src/tools/data_washer_new.py | 1383 +++++++++++++ .../tools/merge_data.py | 107 +- {tools => src/tools}/package.py | 17 +- src/ui/__init__.py | 0 .../ui/dark_mode_style_fix.py | 0 input_panel_ui.py => src/ui/input_panel_ui.py | 163 +- .../ui/similar_history_match_ui.py | 86 +- src/utils/__init__.py | 0 unit.py => src/utils/unit.py | 2 +- .../extract_webm_frames.py | 92 - tools/data_washer_new.py | 904 --------- uv.lock | 1801 +++++++++++------ {maafw => vendor/bin/maafw}/DirectML.dll | Bin .../bin/maafw}/MaaAdbControlUnit.dll | Bin .../bin/maafw}/MaaAgentClient.dll | Bin .../bin/maafw}/MaaAgentServer.dll | Bin .../bin/maafw}/MaaCustomControlUnit.dll | Bin {maafw => vendor/bin/maafw}/MaaFramework.dll | Bin .../bin/maafw}/MaaGamepadControlUnit.dll | Bin {maafw => vendor/bin/maafw}/MaaNode.node | Bin .../bin/maafw}/MaaNodeServer.node | Bin {maafw => vendor/bin/maafw}/MaaPiCli.exe | Bin .../bin/maafw}/MaaRecordControlUnit.dll | Bin .../bin/maafw}/MaaReplayControlUnit.dll | Bin {maafw => vendor/bin/maafw}/MaaToolkit.dll | Bin {maafw => vendor/bin/maafw}/MaaUtils.dll | Bin .../bin/maafw}/MaaWin32ControlUnit.dll | Bin {maafw => vendor/bin/maafw}/ViGEmClient.dll | Bin .../bin/maafw}/fastdeploy_ppocr_maa.dll | Bin .../bin/maafw}/onnxruntime_maa.dll | Bin .../bin/maafw}/opencv_world4_maa.dll | Bin .../bin/maafw}/plugins/MaaPluginDemo.dll | Bin .../bin/platform-tools}/AdbWinApi.dll | Bin .../bin/platform-tools}/AdbWinUsbApi.dll | Bin .../bin/platform-tools}/NOTICE.txt | 0 .../bin/platform-tools}/adb.exe | Bin .../bin/platform-tools}/etc1tool.exe | Bin .../bin/platform-tools}/fastboot.exe | Bin .../bin/platform-tools}/hprof-conv.exe | Bin .../bin/platform-tools}/libwinpthread-1.dll | Bin .../bin/platform-tools}/make_f2fs.exe | Bin .../platform-tools}/make_f2fs_casefold.exe | Bin .../bin/platform-tools}/mke2fs.conf | 0 .../bin/platform-tools}/mke2fs.exe | Bin .../bin/platform-tools}/source.properties | 0 .../bin/platform-tools}/sqlite3.exe | Bin 299 files changed, 9042 insertions(+), 5697 deletions(-) delete mode 100644 WinningRate_Statistics.py rename "\343\200\220\350\204\232\346\234\254\343\200\221\351\223\201\351\262\250\351\261\274\345\220\257\345\212\250\357\274\201(\345\270\246PyTorch\347\211\210\346\234\254\351\200\211\346\213\251\347\211\210).bat" => iron_shark_launcher.bat (62%) delete mode 100644 main_old.py delete mode 100644 models/update.md delete mode 100644 simulator/.gitignore delete mode 100644 simulator/scene.json delete mode 100644 simulator/stats.py delete mode 100644 specialmonster.py rename {models => src}/__init__.py (100%) rename {simulator => src/analysis}/__init__.py (100%) rename sim_mc.py => src/analysis/sim_mc.py (67%) rename similar_history_match.py => src/analysis/similar_history_match.py (74%) create mode 100644 src/analysis/winning_rate_statistics.py rename "data_train/package/\346\225\260\346\215\256\345\214\205\346\224\276\345\234\250\346\255\244\350\267\257\345\276\204.md" => src/core/__init__.py (100%) rename config.py => src/core/config.py (54%) rename constants.py => src/core/constants.py (89%) create mode 100644 src/core/paths.py create mode 100644 src/data/__init__.py rename data_package.py => src/data/data_package.py (96%) rename loadData.py => src/data/load_data.py (76%) create mode 100644 src/game/__init__.py rename auto_fetch.py => src/game/auto_fetch.py (74%) rename login.py => src/game/login.py (72%) rename maa_adb_connector.py => src/game/maa_adb_connector.py (82%) rename winrt_capture.py => src/game/winrt_capture.py (90%) create mode 100644 src/models/__init__.py rename {models => src/models}/model.py (94%) rename {models => src/models}/muon.py (75%) rename predict.py => src/models/predict.py (61%) rename predict_onnx.py => src/models/predict_onnx.py (74%) rename train.py => src/models/train.py (70%) rename val.py => src/models/val.py (71%) create mode 100644 src/recognition/__init__.py rename field_recognition.py => src/recognition/field_recognition.py (68%) rename find_monster_zone.py => src/recognition/find_monster_zone.py (58%) rename recognize.py => src/recognition/recognize.py (81%) create mode 100644 src/recognition/specialmonster.py rename {ico => src/resources/assets/icons}/background.png (100%) rename {ico => src/resources/assets/icons}/icon.ico (100%) rename {ico => src/resources/assets/icons}/icon_64x64.ico (100%) rename "images/R-11\347\252\201\345\207\273\345\212\250\345\212\233\350\243\205\347\224\262.png" => "src/resources/assets/images/R-11\347\252\201\345\207\273\345\212\250\345\212\233\350\243\205\347\224\262.png" (100%) rename "images/R-31\351\207\215\345\236\213\345\212\250\345\212\233\350\243\205\347\224\262.png" => "src/resources/assets/images/R-31\351\207\215\345\236\213\345\212\250\345\212\233\350\243\205\347\224\262.png" (100%) rename {images => src/resources/assets/images}/eg.png (100%) rename {images => src/resources/assets/images}/empty.png (100%) rename {images => src/resources/assets/images}/login/announcement_close.png (100%) rename {images => src/resources/assets/images}/login/competition_page.png (100%) rename {images => src/resources/assets/images}/login/event_claim_close.png (100%) rename {images => src/resources/assets/images}/login/login_button.png (100%) rename {images => src/resources/assets/images}/login/login_off.png (100%) rename {images => src/resources/assets/images}/process/0.png (100%) rename {images => src/resources/assets/images}/process/1.png (100%) rename {images => src/resources/assets/images}/process/10.png (100%) rename {images => src/resources/assets/images}/process/11.png (100%) rename {images => src/resources/assets/images}/process/12.png (100%) rename {images => src/resources/assets/images}/process/13.png (100%) rename {images => src/resources/assets/images}/process/14.png (100%) rename {images => src/resources/assets/images}/process/15.png (100%) rename {images => src/resources/assets/images}/process/1746774840_28_5.png1s.png (100%) rename {images => src/resources/assets/images}/process/2.png (100%) rename {images => src/resources/assets/images}/process/3.png (100%) rename {images => src/resources/assets/images}/process/4.png (100%) rename {images => src/resources/assets/images}/process/5.png (100%) rename {images => src/resources/assets/images}/process/6.png (100%) rename {images => src/resources/assets/images}/process/7.png (100%) rename {images => src/resources/assets/images}/process/8.png (100%) rename {images => src/resources/assets/images}/process/9.png (100%) rename "images/\342\200\234\345\244\215\344\273\207\350\200\205\342\200\235.png" => "src/resources/assets/images/\342\200\234\345\244\215\344\273\207\350\200\205\342\200\235.png" (100%) rename "images/\342\200\234\345\246\222\342\200\235.png" => "src/resources/assets/images/\342\200\234\345\246\222\342\200\235.png" (100%) rename "images/\342\200\234\345\267\242\347\251\264\342\200\235.png" => "src/resources/assets/images/\342\200\234\345\267\242\347\251\264\342\200\235.png" (100%) rename "images/\342\200\234\345\272\236\350\264\235\342\200\235.png" => "src/resources/assets/images/\342\200\234\345\272\236\350\264\235\342\200\235.png" (100%) rename "images/\342\200\234\346\212\225\347\237\263\346\234\272\342\200\235.png" => "src/resources/assets/images/\342\200\234\346\212\225\347\237\263\346\234\272\342\200\235.png" (100%) rename "images/\342\200\234\347\233\233\346\200\222\342\200\235.png" => "src/resources/assets/images/\342\200\234\347\233\233\346\200\222\342\200\235.png" (100%) rename "images/\342\200\234\350\207\252\345\234\250\342\200\235.png" => "src/resources/assets/images/\342\200\234\350\207\252\345\234\250\342\200\235.png" (100%) rename "images/\342\200\234\350\220\250\347\261\263\347\232\204\346\204\217\345\277\227\342\200\235.png" => "src/resources/assets/images/\342\200\234\350\220\250\347\261\263\347\232\204\346\204\217\345\277\227\342\200\235.png" (100%) rename "images/\342\200\234\351\222\263\351\222\263\347\224\237\351\243\216\342\200\235.png" => "src/resources/assets/images/\342\200\234\351\222\263\351\222\263\347\224\237\351\243\216\342\200\235.png" (100%) rename "images/\342\200\234\351\227\250\342\200\235.png" => "src/resources/assets/images/\342\200\234\351\227\250\342\200\235.png" (100%) rename "images/\342\200\234\351\230\277\345\222\254\342\200\235.png" => "src/resources/assets/images/\342\200\234\351\230\277\345\222\254\342\200\235.png" (100%) rename "images/\344\271\214\350\220\250\346\226\257\347\252\201\350\242\255\345\274\251\346\211\213.png" => "src/resources/assets/images/\344\271\214\350\220\250\346\226\257\347\252\201\350\242\255\345\274\251\346\211\213.png" (100%) rename "images/\345\205\250\345\260\201\351\227\255\346\262\231\346\273\251\350\275\246.png" => "src/resources/assets/images/\345\205\250\345\260\201\351\227\255\346\262\231\346\273\251\350\275\246.png" (100%) rename "images/\345\206\260\345\216\237\345\215\253\346\263\225\350\200\205.png" => "src/resources/assets/images/\345\206\260\345\216\237\345\215\253\346\263\225\350\200\205.png" (100%) rename "images/\345\206\260\345\216\237\346\234\257\345\270\210.png" => "src/resources/assets/images/\345\206\260\345\216\237\346\234\257\345\270\210.png" (100%) rename "images/\345\206\260\347\210\206\346\272\220\347\237\263\350\231\253.png" => "src/resources/assets/images/\345\206\260\347\210\206\346\272\220\347\237\263\350\231\253.png" (100%) rename "images/\345\207\213\351\233\266\351\252\221\345\243\253.png" => "src/resources/assets/images/\345\207\213\351\233\266\351\252\221\345\243\253.png" (100%) rename "images/\345\207\266\350\261\225\345\205\275.png" => "src/resources/assets/images/\345\207\266\350\261\225\345\205\275.png" (100%) rename "images/\345\217\215\350\243\205\347\224\262\346\255\245\345\205\265.png" => "src/resources/assets/images/\345\217\215\350\243\205\347\224\262\346\255\245\345\205\265.png" (100%) rename "images/\345\217\230\345\274\202\345\267\250\345\262\251\350\233\233.png" => "src/resources/assets/images/\345\217\230\345\274\202\345\267\250\345\262\251\350\233\233.png" (100%) rename "images/\345\221\274\345\225\270\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222.png" => "src/resources/assets/images/\345\221\274\345\225\270\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222.png" (100%) rename "images/\345\233\272\346\265\267\345\207\277\347\237\263\350\200\205.png" => "src/resources/assets/images/\345\233\272\346\265\267\345\207\277\347\237\263\350\200\205.png" (100%) rename "images/\345\244\215\344\273\207\350\200\205.png" => "src/resources/assets/images/\345\244\215\344\273\207\350\200\205.png" (100%) rename "images/\345\245\216\351\232\206\357\274\214\346\221\251\350\257\203\350\220\250\345\237\265\346\235\203\345\214\226.png" => "src/resources/assets/images/\345\245\216\351\232\206\357\274\214\346\221\251\350\257\203\350\220\250\345\237\265\346\235\203\345\214\226.png" (100%) rename "images/\345\256\277\344\270\273\346\265\201\346\265\252\350\200\205.png" => "src/resources/assets/images/\345\256\277\344\270\273\346\265\201\346\265\252\350\200\205.png" (100%) rename "images/\345\257\273\350\267\257\350\200\205\344\277\241\344\275\277.png" => "src/resources/assets/images/\345\257\273\350\267\257\350\200\205\344\277\241\344\275\277.png" (100%) rename "images/\345\261\261\346\265\267\344\274\227\347\252\245\351\255\205\344\272\272.png" => "src/resources/assets/images/\345\261\261\346\265\267\344\274\227\347\252\245\351\255\205\344\272\272.png" (100%) rename "images/\345\262\201\347\233\270.png" => "src/resources/assets/images/\345\262\201\347\233\270.png" (100%) rename "images/\345\262\251\345\206\240\345\205\275.png" => "src/resources/assets/images/\345\262\251\345\206\240\345\205\275.png" (100%) rename "images/\345\274\247\345\205\211\351\224\213\345\215\253\351\225\277.png" => "src/resources/assets/images/\345\274\247\345\205\211\351\224\213\345\215\253\351\225\277.png" (100%) rename "images/\346\213\263\346\211\213\345\233\232\347\212\257.png" => "src/resources/assets/images/\346\213\263\346\211\213\345\233\232\347\212\257.png" (100%) rename "images/\346\217\220\344\272\232\345\215\241\344\271\214\345\245\275\346\210\230\350\200\205.png" => "src/resources/assets/images/\346\217\220\344\272\232\345\215\241\344\271\214\345\245\275\346\210\230\350\200\205.png" (100%) rename "images/\346\217\220\344\272\232\345\215\241\344\271\214\347\240\264\345\235\217\347\216\213.png" => "src/resources/assets/images/\346\217\220\344\272\232\345\215\241\344\271\214\347\240\264\345\235\217\347\216\213.png" (100%) rename "images/\346\225\243\345\215\216\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222.png" => "src/resources/assets/images/\346\225\243\345\215\216\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222.png" (100%) rename "images/\346\227\272\350\264\242.png" => "src/resources/assets/images/\346\227\272\350\264\242.png" (100%) rename "images/\346\232\264\350\265\260\351\243\237\344\272\272\350\212\261.png" => "src/resources/assets/images/\346\232\264\350\265\260\351\243\237\344\272\272\350\212\261.png" (100%) rename "images/\346\232\264\350\265\260\351\243\237\350\231\253\350\212\261.png" => "src/resources/assets/images/\346\232\264\350\265\260\351\243\237\350\231\253\350\212\261.png" (100%) rename "images/\346\234\227\345\247\206\351\205\222\346\216\250\350\215\220\350\200\205.png" => "src/resources/assets/images/\346\234\227\345\247\206\351\205\222\346\216\250\350\215\220\350\200\205.png" (100%) rename "images/\346\235\260\346\226\257\351\241\277\302\267\345\250\201\345\273\211\345\247\206\346\226\257.png" => "src/resources/assets/images/\346\235\260\346\226\257\351\241\277\302\267\345\250\201\345\273\211\345\247\206\346\226\257.png" (100%) rename "images/\346\236\201\346\270\251\350\222\270\346\261\275\346\210\230\350\275\246.png" => "src/resources/assets/images/\346\236\201\346\270\251\350\222\270\346\261\275\346\210\230\350\275\246.png" (100%) rename "images/\346\236\243\345\244\247\345\210\200.png" => "src/resources/assets/images/\346\236\243\345\244\247\345\210\200.png" (100%) rename "images/\346\236\257\346\234\275\344\271\213\347\247\215.png" => "src/resources/assets/images/\346\236\257\346\234\275\344\271\213\347\247\215.png" (100%) rename "images/\346\243\230\345\205\275.png" => "src/resources/assets/images/\346\243\230\345\205\275.png" (100%) rename "images/\346\256\213\345\205\232\344\271\220\345\233\242\351\274\223\346\211\213.png" => "src/resources/assets/images/\346\256\213\345\205\232\344\271\220\345\233\242\351\274\223\346\211\213.png" (100%) rename "images/\346\256\213\345\205\232\350\220\250\345\205\213\346\226\257\346\211\213.png" => "src/resources/assets/images/\346\256\213\345\205\232\350\220\250\345\205\213\346\226\257\346\211\213.png" (100%) rename "images/\346\260\264\346\211\213\351\207\215\350\211\207.png" => "src/resources/assets/images/\346\260\264\346\211\213\351\207\215\350\211\207.png" (100%) rename "images/\346\260\264\351\201\201\345\277\215\350\200\205.png" => "src/resources/assets/images/\346\260\264\351\201\201\345\277\215\350\200\205.png" (100%) rename "images/\346\262\211\346\262\231.png" => "src/resources/assets/images/\346\262\211\346\262\231.png" (100%) rename "images/\346\262\270\350\241\200\351\252\221\345\243\253\345\233\242\347\262\276\351\224\220.png" => "src/resources/assets/images/\346\262\270\350\241\200\351\252\221\345\243\253\345\233\242\347\262\276\351\224\220.png" (100%) rename "images/\346\263\245\345\262\251\345\260\217\351\230\237\350\267\265\350\241\214\350\200\205.png" => "src/resources/assets/images/\346\263\245\345\262\251\345\260\217\351\230\237\350\267\265\350\241\214\350\200\205.png" (100%) rename "images/\346\263\245\345\262\251\345\267\250\345\203\217.png" => "src/resources/assets/images/\346\263\245\345\262\251\345\267\250\345\203\217.png" (100%) rename "images/\346\267\261\346\272\237\350\243\202\347\244\201\350\200\205.png" => "src/resources/assets/images/\346\267\261\346\272\237\350\243\202\347\244\201\350\200\205.png" (100%) rename "images/\346\270\251\351\241\272\347\232\204\346\255\246\350\243\205\351\251\256\345\205\275.png" => "src/resources/assets/images/\346\270\251\351\241\272\347\232\204\346\255\246\350\243\205\351\251\256\345\205\275.png" (100%) rename "images/\346\270\270\345\207\273\351\230\237\347\233\276\345\215\253.png" => "src/resources/assets/images/\346\270\270\345\207\273\351\230\237\347\233\276\345\215\253.png" (100%) rename "images/\346\271\226\347\225\224\345\277\227\346\204\277\350\200\205.png" => "src/resources/assets/images/\346\271\226\347\225\224\345\277\227\346\204\277\350\200\205.png" (100%) rename "images/\346\272\220\347\237\263\347\225\270\345\217\230\344\275\223.png" => "src/resources/assets/images/\346\272\220\347\237\263\347\225\270\345\217\230\344\275\223.png" (100%) rename "images/\347\201\274\347\203\255\346\272\220\347\237\263\350\231\253.png" => "src/resources/assets/images/\347\201\274\347\203\255\346\272\220\347\237\263\350\231\253.png" (100%) rename "images/\347\202\255\351\225\277\347\237\233.png" => "src/resources/assets/images/\347\202\255\351\225\277\347\237\233.png" (100%) rename "images/\347\202\256\345\207\273\347\273\204\351\225\277.png" => "src/resources/assets/images/\347\202\256\345\207\273\347\273\204\351\225\277.png" (100%) rename "images/\347\202\275\347\204\260\346\272\220\347\237\263\350\231\253.png" => "src/resources/assets/images/\347\202\275\347\204\260\346\272\220\347\237\263\350\231\253.png" (100%) rename "images/\347\213\202\346\232\264\345\256\277\344\270\273\347\273\204\351\225\277.png" => "src/resources/assets/images/\347\213\202\346\232\264\345\256\277\344\270\273\347\273\204\351\225\277.png" (100%) rename "images/\347\213\202\346\232\264\347\232\204\347\214\216\347\213\227pro.png" => "src/resources/assets/images/\347\213\202\346\232\264\347\232\204\347\214\216\347\213\227pro.png" (100%) rename "images/\347\213\231\345\207\273\346\255\245\345\205\265.png" => "src/resources/assets/images/\347\213\231\345\207\273\346\255\245\345\205\265.png" (100%) rename "images/\347\216\211\345\217\214\345\211\221.png" => "src/resources/assets/images/\347\216\211\345\217\214\345\211\221.png" (100%) rename "images/\347\224\260\351\274\267.png" => "src/resources/assets/images/\347\224\260\351\274\267.png" (100%) rename "images/\347\225\270\345\217\230\350\265\230\347\224\237\347\211\251.png" => "src/resources/assets/images/\347\225\270\345\217\230\350\265\230\347\224\237\347\211\251.png" (100%) rename "images/\347\230\264.png" => "src/resources/assets/images/\347\230\264.png" (100%) rename "images/\347\237\277\350\204\211\345\256\210\345\215\253.png" => "src/resources/assets/images/\347\237\277\350\204\211\345\256\210\345\215\253.png" (100%) rename "images/\347\240\201\345\244\264\346\260\264\346\211\213.png" => "src/resources/assets/images/\347\240\201\345\244\264\346\260\264\346\211\213.png" (100%) rename "images/\347\241\254\347\224\262\347\210\252\345\205\275.png" => "src/resources/assets/images/\347\241\254\347\224\262\347\210\252\345\205\275.png" (100%) rename "images/\347\251\272\351\231\215\345\205\265.png" => "src/resources/assets/images/\347\251\272\351\231\215\345\205\265.png" (100%) rename "images/\347\273\210\346\233\262\345\220\210\345\243\260.png" => "src/resources/assets/images/\347\273\210\346\233\262\345\220\210\345\243\260.png" (100%) rename "images/\350\205\220\350\264\245\351\252\221\345\243\253.png" => "src/resources/assets/images/\350\205\220\350\264\245\351\252\221\345\243\253.png" (100%) rename "images/\350\207\252\347\224\261\344\275\243\345\205\265\342\200\234\344\270\273\351\237\263\345\220\211\344\273\226\342\200\235.png" => "src/resources/assets/images/\350\207\252\347\224\261\344\275\243\345\205\265\342\200\234\344\270\273\351\237\263\345\220\211\344\273\226\342\200\235.png" (100%) rename "images/\350\207\252\347\224\261\344\275\243\345\205\265\342\200\234\350\264\235\346\226\257\342\200\235.png" => "src/resources/assets/images/\350\207\252\347\224\261\344\275\243\345\205\265\342\200\234\350\264\235\346\226\257\342\200\235.png" (100%) rename "images/\350\215\222\345\216\237\345\212\253\346\216\240\350\200\205.png" => "src/resources/assets/images/\350\215\222\345\216\237\345\212\253\346\216\240\350\200\205.png" (100%) rename "images/\350\220\250\345\215\241\345\205\271\345\244\247\345\211\221\346\211\213.png" => "src/resources/assets/images/\350\220\250\345\215\241\345\205\271\345\244\247\345\211\221\346\211\213.png" (100%) rename "images/\350\220\250\345\215\241\345\205\271\345\256\277\344\270\273\345\215\253\345\267\242\347\231\276\345\244\253\351\225\277.png" => "src/resources/assets/images/\350\220\250\345\215\241\345\205\271\345\256\277\344\270\273\345\215\253\345\267\242\347\231\276\345\244\253\351\225\277.png" (100%) rename "images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\345\220\236\345\231\254\350\200\205.png" => "src/resources/assets/images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\345\220\236\345\231\254\350\200\205.png" (100%) rename "images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\345\256\277\345\215\253.png" => "src/resources/assets/images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\345\256\277\345\215\253.png" (100%) rename "images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\346\210\230\350\275\246.png" => "src/resources/assets/images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\346\210\230\350\275\246.png" (100%) rename "images/\350\257\241\346\240\270\351\233\206\345\205\273\350\200\205.png" => "src/resources/assets/images/\350\257\241\346\240\270\351\233\206\345\205\273\350\200\205.png" (100%) rename "images/\350\265\217\351\207\221\347\214\216\344\272\272\345\274\251\346\211\213.png" => "src/resources/assets/images/\350\265\217\351\207\221\347\214\216\344\272\272\345\274\251\346\211\213.png" (100%) rename "images/\350\265\217\351\207\221\347\214\216\344\272\272\346\211\260\344\271\261\350\200\205.png" => "src/resources/assets/images/\350\265\217\351\207\221\347\214\216\344\272\272\346\211\260\344\271\261\350\200\205.png" (100%) rename "images/\350\277\267\350\267\257\347\232\204\345\267\250\345\203\217.png" => "src/resources/assets/images/\350\277\267\350\267\257\347\232\204\345\267\250\345\203\217.png" (100%) rename "images/\351\205\270\346\266\262\346\272\220\347\237\263\350\231\253\302\267\316\261.png" => "src/resources/assets/images/\351\205\270\346\266\262\346\272\220\347\237\263\350\231\253\302\267\316\261.png" (100%) rename "images/\351\207\215\350\243\205\351\230\262\345\276\241\350\200\205.png" => "src/resources/assets/images/\351\207\215\350\243\205\351\230\262\345\276\241\350\200\205.png" (100%) rename "images/\351\230\265\345\234\260\345\207\273\344\272\272\346\211\213.png" => "src/resources/assets/images/\351\230\265\345\234\260\345\207\273\344\272\272\346\211\213.png" (100%) rename "images/\351\243\216\346\203\205\350\241\227\342\200\234\346\230\237\346\234\257\345\270\210\342\200\235.png" => "src/resources/assets/images/\351\243\216\346\203\205\350\241\227\342\200\234\346\230\237\346\234\257\345\270\210\342\200\235.png" (100%) rename "images/\351\253\230\345\241\224\346\234\257\345\270\210.png" => "src/resources/assets/images/\351\253\230\345\241\224\346\234\257\345\270\210.png" (100%) rename "images/\351\253\230\346\231\256\345\260\274\345\205\213.png" => "src/resources/assets/images/\351\253\230\346\231\256\345\260\274\345\205\213.png" (100%) rename "images/\351\253\230\347\272\247\346\255\246\350\243\205\344\272\272\345\221\230.png" => "src/resources/assets/images/\351\253\230\347\272\247\346\255\246\350\243\205\344\272\272\345\221\230.png" (100%) rename "images/\351\253\230\350\203\275\346\272\220\347\237\263\350\231\253.png" => "src/resources/assets/images/\351\253\230\350\203\275\346\272\220\347\237\263\350\231\253.png" (100%) rename "images/\351\273\221\346\260\264\346\272\220\347\237\263\350\231\253.png" => "src/resources/assets/images/\351\273\221\346\260\264\346\272\220\347\237\263\350\231\253.png" (100%) create mode 100644 src/resources/config/maa_option.json rename monster.csv => src/resources/data/monster.csv (100%) rename monster_greenvine.csv => src/resources/data/monster_greenvine.csv (100%) rename {simulator => src/simulation}/README.md (100%) create mode 100644 src/simulation/__init__.py rename {simulator => src/simulation}/arknights.csv (100%) rename {simulator => src/simulation}/battle_field.py (60%) rename {simulator => src/simulation}/elemental.py (82%) rename {simulator => src/simulation}/monsters.json (100%) rename {simulator => src/simulation}/monsters.py (80%) rename {simulator => src/simulation}/projectiles.py (52%) create mode 100644 src/simulation/scene.json rename {simulator => src/simulation}/simulate.py (60%) create mode 100644 src/simulation/stats.py rename {simulator => src/simulation}/utils.py (80%) rename {simulator => src/simulation}/vector2d.py (85%) rename {simulator => src/simulation}/zone.py (70%) rename {tools => src/tools}/HumanDataCheck.py (58%) rename {tools => src/tools}/battlefield_composite/.gitignore (100%) rename {tools => src/tools}/battlefield_composite/battlefield_composite.py (87%) create mode 100644 src/tools/battlefield_composite/extract_webm_frames.py rename {tools => src/tools}/battlefield_composite/monster_images/IM-1.png (100%) rename {tools => src/tools}/battlefield_composite/monster_images/battlefield_empty.jpg (100%) rename "tools/battlefield_composite/monster_images/\342\200\234\345\244\215\344\273\207\350\200\205\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\342\200\234\345\244\215\344\273\207\350\200\205\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\342\200\234\345\272\236\350\264\235\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\342\200\234\345\272\236\350\264\235\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\342\200\234\346\212\225\347\237\263\346\234\272\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-A_Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\342\200\234\346\212\225\347\237\263\346\234\272\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-A_Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\342\200\234\350\207\252\345\234\250\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\342\200\234\350\207\252\345\234\250\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\342\200\234\351\222\263\351\222\263\347\224\237\351\243\216\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\342\200\234\351\222\263\351\222\263\347\224\237\351\243\216\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\342\200\234\351\227\250\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\342\200\234\351\227\250\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\342\200\234\351\230\277\345\222\254\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\342\200\234\351\230\277\345\222\254\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\345\206\260\345\216\237\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\345\206\260\345\216\237\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\345\206\260\347\210\206\346\272\220\347\237\263\350\231\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\345\206\260\347\210\206\346\272\220\347\237\263\350\231\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\345\217\215\350\243\205\347\224\262\346\255\245\345\205\265-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_A-x1.webm" => "src/tools/battlefield_composite/monster_images/\345\217\215\350\243\205\347\224\262\346\255\245\345\205\265-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_A-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\345\217\230\345\274\202\345\267\250\345\262\251\350\233\233-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\345\217\230\345\274\202\345\267\250\345\262\251\350\233\233-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\345\221\274\345\225\270\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\345\221\274\345\225\270\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\345\233\272\346\265\267\345\207\277\347\237\263\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\345\233\272\346\265\267\345\207\277\347\237\263\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\345\244\215\344\273\207\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\345\244\215\344\273\207\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\345\256\277\344\270\273\346\213\276\350\215\222\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\345\256\277\344\270\273\346\213\276\350\215\222\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\345\257\214\350\220\245\345\205\273\347\232\204\347\251\277\345\210\272\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\345\257\214\350\220\245\345\205\273\347\232\204\347\251\277\345\210\272\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\345\261\261\346\265\267\344\274\227\347\252\245\351\255\205\344\272\272-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\345\261\261\346\265\267\344\274\227\347\252\245\351\255\205\344\272\272-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\345\261\261\351\233\252\351\254\274-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\345\261\261\351\233\252\351\254\274-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\345\274\247\345\205\211\351\224\213\345\215\253\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\345\274\247\345\205\211\351\224\213\345\215\253\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\211\216\347\275\227\357\274\214\342\200\234\347\213\274\344\271\213\344\270\273\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-A_Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\211\216\347\275\227\357\274\214\342\200\234\347\213\274\344\271\213\344\270\273\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-A_Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\213\263\346\211\213\345\233\232\347\212\257-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_red-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\213\263\346\211\213\345\233\232\347\212\257-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_red-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\217\220\344\272\232\345\215\241\344\271\214\345\245\275\346\210\230\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\217\220\344\272\232\345\215\241\344\271\214\345\245\275\346\210\230\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\217\220\344\272\232\345\215\241\344\271\214\347\240\264\345\235\217\347\216\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\217\220\344\272\232\345\215\241\344\271\214\347\240\264\345\235\217\347\216\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\225\243\345\215\216\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\225\243\345\215\216\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\234\227\345\247\206\351\205\222\346\216\250\350\215\220\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\234\227\345\247\206\351\205\222\346\216\250\350\215\220\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\234\250\350\243\202\346\210\230\345\243\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\234\250\350\243\202\346\210\230\345\243\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\235\260\346\226\257\351\241\277\302\267\345\250\201\345\273\211\345\247\206\346\226\257-\351\273\230\350\256\244-\346\210\230\346\226\227-C1_Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\235\260\346\226\257\351\241\277\302\267\345\250\201\345\273\211\345\247\206\346\226\257-\351\273\230\350\256\244-\346\210\230\346\226\227-C1_Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\237\223\346\261\241\350\272\257\345\243\263-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\237\223\346\261\241\350\272\257\345\243\263-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\256\213\345\205\232\350\220\250\345\205\213\346\226\257\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\256\213\345\205\232\350\220\250\345\205\213\346\226\257\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\262\270\350\241\200\351\252\221\345\243\253\345\233\242\347\262\276\351\224\220-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\262\270\350\241\200\351\252\221\345\243\253\345\233\242\347\262\276\351\224\220-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\263\245\345\262\251\345\267\250\345\203\217-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\263\245\345\262\251\345\267\250\345\203\217-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\267\261\346\272\237\350\243\202\347\244\201\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\267\261\346\272\237\350\243\202\347\244\201\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\270\251\351\241\272\347\232\204\346\255\246\350\243\205\351\251\256\345\205\275-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\270\251\351\241\272\347\232\204\346\255\246\350\243\205\351\251\256\345\205\275-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\271\226\347\225\224\345\277\227\346\204\277\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\271\226\347\225\224\345\277\227\346\204\277\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\272\220\347\237\263\347\225\270\345\217\230\344\275\223-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\272\220\347\237\263\347\225\270\345\217\230\344\275\223-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\347\201\260\345\260\276\351\246\231\344\270\273-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\347\201\260\345\260\276\351\246\231\344\270\273-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\347\202\256\345\207\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Attack-x1.webm" => "src/tools/battlefield_composite/monster_images/\347\202\256\345\207\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Attack-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\347\202\256\345\207\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\347\202\256\345\207\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\347\203\210\351\205\222\347\272\247\351\206\222\351\205\222\345\212\251\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\347\203\210\351\205\222\347\272\247\351\206\222\351\205\222\345\212\251\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\347\213\202\346\232\264\345\256\277\344\270\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\347\213\202\346\232\264\345\256\277\344\270\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\347\213\202\346\232\264\347\232\204\347\214\216\347\213\227pro-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" => "src/tools/battlefield_composite/monster_images/\347\213\202\346\232\264\347\232\204\347\214\216\347\213\227pro-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\347\224\260\351\274\267\345\212\233\345\243\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\347\224\260\351\274\267\345\212\233\345\243\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\347\237\277\350\204\211\345\256\210\345\215\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\347\237\277\350\204\211\345\256\210\345\215\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\347\245\236\345\260\204\346\211\213\345\233\232\347\212\257-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\347\245\236\345\260\204\346\211\213\345\233\232\347\212\257-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\345\244\247\345\211\221\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" => "src/tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\345\244\247\345\211\221\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\345\255\220\350\243\224\351\223\276\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\345\255\220\350\243\224\351\223\276\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\347\216\213\345\272\255\345\206\233\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\347\216\213\345\272\255\345\206\233\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\351\200\220\350\205\220\345\205\275-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\351\200\220\350\205\220\345\205\275-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\351\205\270\346\266\262\346\272\220\347\237\263\350\231\253\302\267\316\261-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" => "src/tools/battlefield_composite/monster_images/\351\205\270\346\266\262\346\272\220\347\237\263\350\231\253\302\267\316\261-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\351\207\215\350\243\205\351\230\262\345\276\241\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" => "src/tools/battlefield_composite/monster_images/\351\207\215\350\243\205\351\230\262\345\276\241\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\351\230\265\345\234\260\345\207\273\344\272\272\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\351\230\265\345\234\260\345\207\273\344\272\272\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\351\243\216\346\203\205\350\241\227\342\200\234\346\230\237\346\234\257\345\270\210\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\351\243\216\346\203\205\350\241\227\342\200\234\346\230\237\346\234\257\345\270\210\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\351\253\230\345\241\224\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\351\253\230\345\241\224\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\351\253\230\346\231\256\345\260\274\345\205\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\351\253\230\346\231\256\345\260\274\345\205\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\351\253\230\347\272\247\346\255\246\350\243\205\344\272\272\345\221\230-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\351\253\230\347\272\247\346\255\246\350\243\205\344\272\272\345\221\230-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\351\253\230\350\203\275\346\272\220\347\237\263\350\231\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\351\253\230\350\203\275\346\272\220\347\237\263\350\231\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename {tools => src/tools}/battlefield_recognize/class_to_idx.json (100%) rename "tools/battlefield_recognize/\345\234\272\345\234\260\350\257\206\345\210\253\345\255\227\346\256\265\350\257\264\346\230\216.txt" => "src/tools/battlefield_recognize/\345\234\272\345\234\260\350\257\206\345\210\253\345\255\227\346\256\265\350\257\264\346\230\216.txt" (100%) rename {tools => src/tools}/convert_model.py (70%) rename {tools => src/tools}/data_cleaning.py (97%) rename {tools => src/tools}/data_cleaning_with_field_recognize.py (61%) rename {tools => src/tools}/data_cleaning_with_field_recognize_gpu.py (61%) rename {tools => src/tools}/data_nanWriter.py (94%) create mode 100644 src/tools/data_washer_new.py rename "data_train/\346\225\260\346\215\256\345\220\210\345\271\266.py" => src/tools/merge_data.py (69%) rename {tools => src/tools}/package.py (93%) create mode 100644 src/ui/__init__.py rename dark_mode_style_fix.py => src/ui/dark_mode_style_fix.py (100%) rename input_panel_ui.py => src/ui/input_panel_ui.py (80%) rename simular_history_match_ui.py => src/ui/similar_history_match_ui.py (85%) create mode 100644 src/utils/__init__.py rename unit.py => src/utils/unit.py (94%) delete mode 100644 tools/battlefield_composite/extract_webm_frames.py delete mode 100644 tools/data_washer_new.py rename {maafw => vendor/bin/maafw}/DirectML.dll (100%) rename {maafw => vendor/bin/maafw}/MaaAdbControlUnit.dll (100%) rename {maafw => vendor/bin/maafw}/MaaAgentClient.dll (100%) rename {maafw => vendor/bin/maafw}/MaaAgentServer.dll (100%) rename {maafw => vendor/bin/maafw}/MaaCustomControlUnit.dll (100%) rename {maafw => vendor/bin/maafw}/MaaFramework.dll (100%) rename {maafw => vendor/bin/maafw}/MaaGamepadControlUnit.dll (100%) rename {maafw => vendor/bin/maafw}/MaaNode.node (100%) rename {maafw => vendor/bin/maafw}/MaaNodeServer.node (100%) rename {maafw => vendor/bin/maafw}/MaaPiCli.exe (100%) rename {maafw => vendor/bin/maafw}/MaaRecordControlUnit.dll (100%) rename {maafw => vendor/bin/maafw}/MaaReplayControlUnit.dll (100%) rename {maafw => vendor/bin/maafw}/MaaToolkit.dll (100%) rename {maafw => vendor/bin/maafw}/MaaUtils.dll (100%) rename {maafw => vendor/bin/maafw}/MaaWin32ControlUnit.dll (100%) rename {maafw => vendor/bin/maafw}/ViGEmClient.dll (100%) rename {maafw => vendor/bin/maafw}/fastdeploy_ppocr_maa.dll (100%) rename {maafw => vendor/bin/maafw}/onnxruntime_maa.dll (100%) rename {maafw => vendor/bin/maafw}/opencv_world4_maa.dll (100%) rename {maafw => vendor/bin/maafw}/plugins/MaaPluginDemo.dll (100%) rename {platform-tools => vendor/bin/platform-tools}/AdbWinApi.dll (100%) rename {platform-tools => vendor/bin/platform-tools}/AdbWinUsbApi.dll (100%) rename {platform-tools => vendor/bin/platform-tools}/NOTICE.txt (100%) rename {platform-tools => vendor/bin/platform-tools}/adb.exe (100%) rename {platform-tools => vendor/bin/platform-tools}/etc1tool.exe (100%) rename {platform-tools => vendor/bin/platform-tools}/fastboot.exe (100%) rename {platform-tools => vendor/bin/platform-tools}/hprof-conv.exe (100%) rename {platform-tools => vendor/bin/platform-tools}/libwinpthread-1.dll (100%) rename {platform-tools => vendor/bin/platform-tools}/make_f2fs.exe (100%) rename {platform-tools => vendor/bin/platform-tools}/make_f2fs_casefold.exe (100%) rename {platform-tools => vendor/bin/platform-tools}/mke2fs.conf (100%) rename {platform-tools => vendor/bin/platform-tools}/mke2fs.exe (100%) rename {platform-tools => vendor/bin/platform-tools}/source.properties (100%) rename {platform-tools => vendor/bin/platform-tools}/sqlite3.exe (100%) diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index 73a4c53..eadd33c 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -24,13 +24,13 @@ jobs: run: uv sync --group dev - name: Build with PyInstaller - run: uv run tools/package.py + run: uv run src/tools/package.py - name: Upload build artifact uses: actions/upload-artifact@v7 with: name: cannotmax-build - path: output/main/ + path: build/dist/main/ retention-days: 1 release: diff --git a/.gitignore b/.gitignore index 5781d3d..14e3469 100644 --- a/.gitignore +++ b/.gitignore @@ -30,27 +30,17 @@ __pycache__/ # 5. 模型权重与构建产物 # =========================== # 模型文件 -models/*.png -*.pth -*.onnx -*.onnx.data +/models/ # 构建与输出目录 -build/ -output/ +/build/ # =========================== # 6. 数据集与媒体资源 # =========================== # 基础数据目录 -data/images/ -data/[0-9][0-9][0-9][0-9]_[0-1][0-9]_[0-3][0-9]__[0-2][0-9]_[0-5][0-9]_[0-5][0-9]/ -images/nums/ - -# 训练相关数据 -data_train/tmp/ -data_train/package/*.zip -data_train/images/ +src/resources/assets/images/nums/ +/data/ # 单独的截图文件 screenshot.png @@ -68,5 +58,4 @@ tmp # =========================== # 8. 其他杂项文件 # =========================== -arknights.csv multi_ports.txt diff --git a/WinningRate_Statistics.py b/WinningRate_Statistics.py deleted file mode 100644 index c6d9aad..0000000 --- a/WinningRate_Statistics.py +++ /dev/null @@ -1,942 +0,0 @@ -import pandas as pd -import numpy as np -from pathlib import Path -from math import sqrt -import csv -from collections import defaultdict -from config import MONSTER_COUNT, FIELD_FEATURE_COUNT, MONSTER_DATA - -FIELD_FEATURE_COUNT=0 -def load_data(): - """加载数据""" - df = pd.read_csv('arknights.csv', header=None, low_memory=False) - # 设置列名 - monster_cols_left = [f'L{i+1}' for i in range(MONSTER_COUNT)] - field_cols_left = [f'FL{i+1}' for i in range(FIELD_FEATURE_COUNT)] - monster_cols_right = [f'R{i+1}' for i in range(MONSTER_COUNT)] - field_cols_right = [f'FR{i+1}' for i in range(FIELD_FEATURE_COUNT)] - - df.columns = monster_cols_left + field_cols_left + monster_cols_right + field_cols_right + ['Result', 'ImgPath'] - return df - - -def get_monster_name(monster_id): - """根据怪物ID获取怪物名称""" - if monster_id in MONSTER_DATA.index: - return MONSTER_DATA.loc[monster_id]['名称'] - return f'怪物{monster_id}' - - -def get_monster_original_name(monster_id): - """根据怪物ID获取怪物原始名称(用于匹配图片)""" - if monster_id in MONSTER_DATA.index: - return MONSTER_DATA.loc[monster_id]['原始名称'] - return f'怪物{monster_id}' - - -def calculate_all_monster_win_rates(df): - """计算所有怪物的胜率""" - monster_stats = {} - total_matches = len(df) - - for i in range(1, MONSTER_COUNT + 1): - try: - # 转换数据类型并过滤 - df[f'L{i}'] = pd.to_numeric(df[f'L{i}'], errors='coerce').fillna(0) - df[f'R{i}'] = pd.to_numeric(df[f'R{i}'], errors='coerce').fillna(0) - - # 左方统计 - left_games = df[df[f'L{i}'] != 0] - left_wins = len(left_games[left_games['Result'] == 'L']) - left_total = len(left_games) - - # 右方统计 - right_games = df[df[f'R{i}'] != 0] - right_wins = len(right_games[right_games['Result'] == 'R']) - right_total = len(right_games) - except Exception as e: - print(f"处理怪物{i}时出错: {e}") - continue - - # 合并统计 - total_games = left_total + right_total - total_wins = left_wins + right_wins - - if total_games > 0: - monster_name = get_monster_name(i) - win_rate = total_wins / total_games - participation_rate = total_games / total_matches if total_matches > 0 else 0 - monster_stats[monster_name] = { - '怪物ID': i, - '胜场': total_wins, - '总场数': total_games, - '胜率': win_rate, - '参战率': participation_rate - } - - return pd.DataFrame(monster_stats).T.sort_values('胜率', ascending=False) - - -def analyze_monster_combinations(df): - """分析怪物配合效果""" - # 初始化数据结构 - single_stats = defaultdict(lambda: {'appearances': 0, 'wins': 0}) - pair_stats = defaultdict(lambda: {'co_occurrences': 0, 'co_wins': 0}) - - for _, record in df.iterrows(): - victory_side = record['Result'] - - # 获取左右两方的怪物 - left_monsters = [] - right_monsters = [] - - for i in range(1, MONSTER_COUNT + 1): - try: - left_val = float(record[f'L{i}']) if pd.notna(record[f'L{i}']) else 0 - right_val = float(record[f'R{i}']) if pd.notna(record[f'R{i}']) else 0 - - if left_val > 0: - left_monsters.append(i) - if right_val > 0: - right_monsters.append(i) - except (ValueError, TypeError): - continue - - # 确定胜利队伍和失败队伍 - if victory_side == 'L': - win_team, lose_team = left_monsters, right_monsters - else: - win_team, lose_team = right_monsters, left_monsters - - # 更新单怪统计(胜利方) - for monster in win_team: - single_stats[monster]['appearances'] += 1 - single_stats[monster]['wins'] += 1 - - # 更新单怪统计(失败方) - for monster in lose_team: - single_stats[monster]['appearances'] += 1 - - # 更新双怪组合统计(胜利方) - for i in range(len(win_team)): - for j in range(i+1, len(win_team)): - x, y = sorted((win_team[i], win_team[j])) - pair_stats[(x, y)]['co_occurrences'] += 1 - pair_stats[(x, y)]['co_wins'] += 1 - - # 更新双怪组合统计(失败方) - for i in range(len(lose_team)): - for j in range(i+1, len(lose_team)): - x, y = sorted((lose_team[i], lose_team[j])) - pair_stats[(x, y)]['co_occurrences'] += 1 - - # 计算最佳配合 - results = [] - total_battles = len(df) - - for (x, y), stats in pair_stats.items(): - if stats['co_occurrences'] < 10: # 过滤低频组合 - continue - - # 组合胜率 - xy_win_rate = stats['co_wins'] / stats['co_occurrences'] - - # 单怪胜率 - if single_stats[x]['appearances'] > 0 and single_stats[y]['appearances'] > 0: - x_win_rate = single_stats[x]['wins'] / single_stats[x]['appearances'] - y_win_rate = single_stats[y]['wins'] / single_stats[y]['appearances'] - - # 提升度 - 简化计算,不使用卡方检验 - expected_win_rate = sqrt(x_win_rate * y_win_rate) - lift = xy_win_rate / expected_win_rate if expected_win_rate > 0 else 0 - - if lift > 1.1 and xy_win_rate > max(x_win_rate, y_win_rate) and stats['co_occurrences'] >= 20: - x_name = get_monster_name(x) - y_name = get_monster_name(y) - results.append({ - '组合': f'{x_name}+{y_name}', - '怪物1': x_name, - '怪物2': y_name, - 'ID1': x, - 'ID2': y, - '提升度': lift, - '组合胜率': xy_win_rate, - '出场次数': stats['co_occurrences'], - '获胜次数': stats['co_wins'] - }) - - # 按提升度排序 - results.sort(key=lambda x: -x['提升度']) - return pd.DataFrame(results) - - -def find_countered_monsters(df): - """寻找被克制的怪物前五个""" - counter_stats = defaultdict(lambda: {'total_matchups': 0, 'losses': 0}) - - for _, record in df.iterrows(): - victory_side = record['Result'] - - # 获取左右两方的怪物 - left_monsters = [] - right_monsters = [] - - for i in range(1, MONSTER_COUNT + 1): - try: - left_val = float(record[f'L{i}']) if pd.notna(record[f'L{i}']) else 0 - right_val = float(record[f'R{i}']) if pd.notna(record[f'R{i}']) else 0 - - if left_val > 0: - left_monsters.append(i) - if right_val > 0: - right_monsters.append(i) - except (ValueError, TypeError): - continue - - # 分析对战情况 - for left_monster in left_monsters: - for right_monster in right_monsters: - # 左方怪物的统计 - counter_stats[left_monster]['total_matchups'] += 1 - if victory_side == 'R': # 左方败北 - counter_stats[left_monster]['losses'] += 1 - - # 右方怪物的统计 - counter_stats[right_monster]['total_matchups'] += 1 - if victory_side == 'L': # 右方败北 - counter_stats[right_monster]['losses'] += 1 - - # 计算被克制率 - countered_results = [] - for monster_id, stats in counter_stats.items(): - if stats['total_matchups'] >= 20: # 至少20场对战 - loss_rate = stats['losses'] / stats['total_matchups'] - monster_name = get_monster_name(monster_id) - countered_results.append({ - '怪物': monster_name, - '怪物ID': monster_id, - '被克制率': loss_rate, - '败场': stats['losses'], - '总对战数': stats['total_matchups'] - }) - - # 按被克制率排序(降序) - countered_results.sort(key=lambda x: -x['被克制率']) - return pd.DataFrame(countered_results[:5]) - - -def analyze_individual_monster_relations(df): - """分析每个怪物的详细关系:最佳队友、克制关系、被克制关系""" - monster_relations = {} - - # 初始化数据结构 - single_stats = defaultdict(lambda: {'appearances': 0, 'wins': 0}) - pair_stats = defaultdict(lambda: {'co_occurrences': 0, 'co_wins': 0}) - counter_stats = defaultdict(lambda: defaultdict(lambda: {'matchups': 0, 'wins': 0})) - - for _, record in df.iterrows(): - victory_side = record['Result'] - - # 获取左右两方的怪物 - left_monsters = [] - right_monsters = [] - - for i in range(1, MONSTER_COUNT + 1): - try: - left_val = float(record[f'L{i}']) if pd.notna(record[f'L{i}']) else 0 - right_val = float(record[f'R{i}']) if pd.notna(record[f'R{i}']) else 0 - - if left_val > 0: - left_monsters.append(i) - if right_val > 0: - right_monsters.append(i) - except (ValueError, TypeError): - continue - - # 确定胜利队伍和失败队伍 - if victory_side == 'L': - win_team, lose_team = left_monsters, right_monsters - else: - win_team, lose_team = right_monsters, left_monsters - - # 更新单怪统计 - for monster in win_team: - single_stats[monster]['appearances'] += 1 - single_stats[monster]['wins'] += 1 - - for monster in lose_team: - single_stats[monster]['appearances'] += 1 - - # 更新队友统计 - for i in range(len(win_team)): - for j in range(i+1, len(win_team)): - x, y = sorted((win_team[i], win_team[j])) - pair_stats[(x, y)]['co_occurrences'] += 1 - pair_stats[(x, y)]['co_wins'] += 1 - - for i in range(len(lose_team)): - for j in range(i+1, len(lose_team)): - x, y = sorted((lose_team[i], lose_team[j])) - pair_stats[(x, y)]['co_occurrences'] += 1 - - # 更新克制关系统计 - for winner in win_team: - for loser in lose_team: - counter_stats[winner][loser]['matchups'] += 1 - counter_stats[winner][loser]['wins'] += 1 - counter_stats[loser][winner]['matchups'] += 1 - - # 为每个怪物分析关系 - for monster_id in range(1, MONSTER_COUNT + 1): - monster_name = get_monster_name(monster_id) - - if single_stats[monster_id]['appearances'] < 10: # 数据量太少 - continue - - # 分析最佳队友 - best_teammates = [] - for (x, y), stats in pair_stats.items(): - if x == monster_id or y == monster_id: - partner_id = y if x == monster_id else x - if stats['co_occurrences'] >= 5: # 至少5次合作 - combo_win_rate = stats['co_wins'] / stats['co_occurrences'] - - # 计算提升度 - if (single_stats[monster_id]['appearances'] > 0 and - single_stats[partner_id]['appearances'] > 0): - monster_win_rate = single_stats[monster_id]['wins'] / single_stats[monster_id]['appearances'] - partner_win_rate = single_stats[partner_id]['wins'] / single_stats[partner_id]['appearances'] - expected_win_rate = sqrt(monster_win_rate * partner_win_rate) - - if expected_win_rate > 0: - lift = combo_win_rate / expected_win_rate - if lift > 1.0: - best_teammates.append({ - 'partner_id': partner_id, - 'partner_name': get_monster_name(partner_id), - 'lift': lift, - 'combo_win_rate': combo_win_rate, - 'occurrences': stats['co_occurrences'] - }) - - best_teammates.sort(key=lambda x: -x['lift']) - - # 分析克制关系 - counters = [] # 该怪物克制的 - countered_by = [] # 克制该怪物的 - - for opponent_id in counter_stats[monster_id]: - stats = counter_stats[monster_id][opponent_id] - if stats['matchups'] >= 5: - win_rate = stats['wins'] / stats['matchups'] - if win_rate > 0.6: # 胜率超过60%认为克制 - counters.append({ - 'opponent_id': opponent_id, - 'opponent_name': get_monster_name(opponent_id), - 'win_rate': win_rate, - 'matchups': stats['matchups'] - }) - - for opponent_id in range(1, MONSTER_COUNT + 1): - if opponent_id in counter_stats and monster_id in counter_stats[opponent_id]: - stats = counter_stats[opponent_id][monster_id] - if stats['matchups'] >= 5: - lose_rate = stats['wins'] / stats['matchups'] - if lose_rate > 0.6: # 对方胜率超过60%认为被克制 - countered_by.append({ - 'opponent_id': opponent_id, - 'opponent_name': get_monster_name(opponent_id), - 'lose_rate': lose_rate, - 'matchups': stats['matchups'] - }) - - counters.sort(key=lambda x: -x['win_rate']) - countered_by.sort(key=lambda x: -x['lose_rate']) - - monster_relations[monster_id] = { - 'name': monster_name, - 'best_teammates': best_teammates[:3], - 'counters': counters[:3], - 'countered_by': countered_by[:3] - } - - return monster_relations - - -def get_terrain_feature_columns(): - """获取地形特征列名""" - import json - import re - from collections import defaultdict - - try: - # 加载类别映射 - class_map_path = "tools/battlefield_recognize/class_to_idx.json" - with open(class_map_path, 'r', encoding='utf-8') as f: - class_to_idx = json.load(f) - - # 使用与data_cleaning_with_field_recognize_gpu.py相同的逻辑 - grouped_elements = defaultdict(list) - for class_name in class_to_idx.keys(): - if class_name.endswith('_none'): - continue - condensed_name = re.sub(r'_left_', '_', class_name) - condensed_name = re.sub(r'_right_', '_', condensed_name) - grouped_elements[condensed_name].append(class_name) - - # 返回排序后的特征列名 - return sorted(grouped_elements.keys()) - except Exception as e: - print(f"无法获取地形特征列名,使用默认值: {e}") - # 如果无法获取,返回默认列表 - return [ - "altar_vertical_altar", "block_parallel_block", "block_vertical_altar_shape1", - "block_vertical_altar_shape2", "block_vertical_block_shape1", "block_vertical_block_shape2", - "coil_narrow_coil", "coil_wide_coil", "crossbow_top_crossbow", - "fire_side_crossbow", "fire_side_fire", "fire_top_fire" - ] - - -def analyze_terrain_effects(df): - """分析地形对怪物的影响""" - terrain_effects = [] - - # 获取实际的地形特征列名 - terrain_feature_columns = get_terrain_feature_columns() - - # 地形显示名称映射 - terrain_display_mapping = { - "altar_vertical_altar": "垂直祭坛", - "block_parallel_block": "平行方块阻挡", - "block_vertical_altar_shape1": "垂直祭坛形阻挡1", - "block_vertical_altar_shape2": "垂直祭坛形阻挡2", - "block_vertical_block_shape1": "垂直方块阻挡1", - "block_vertical_block_shape2": "垂直方块阻挡2", - "coil_narrow_coil": "窄型线圈装置", - "coil_wide_coil": "宽型线圈装置", - "crossbow_top_crossbow": "顶部弩炮", - "fire_side_crossbow": "侧边弩炮", - "fire_side_fire": "侧边火炮", - "fire_top_fire": "顶部火炮" - } - - for terrain_idx, terrain_key in enumerate(terrain_feature_columns): - terrain_name = terrain_display_mapping.get(terrain_key, terrain_key) - - for monster_idx in range(1, MONSTER_COUNT + 1): - monster_name = get_monster_name(monster_idx) - - # 转换数据类型 - df[f'FL{terrain_idx+1}'] = pd.to_numeric(df[f'FL{terrain_idx+1}'], errors='coerce').fillna(0) - df[f'FR{terrain_idx+1}'] = pd.to_numeric(df[f'FR{terrain_idx+1}'], errors='coerce').fillna(0) - df[f'L{monster_idx}'] = pd.to_numeric(df[f'L{monster_idx}'], errors='coerce').fillna(0) - df[f'R{monster_idx}'] = pd.to_numeric(df[f'R{monster_idx}'], errors='coerce').fillna(0) - - # 有地形时的表现 - terrain_left_games = df[(df[f'FL{terrain_idx+1}'] == 1) & (df[f'L{monster_idx}'] > 0)] - terrain_right_games = df[(df[f'FR{terrain_idx+1}'] == 1) & (df[f'R{monster_idx}'] > 0)] - - terrain_total = len(terrain_left_games) + len(terrain_right_games) - if terrain_total < 5: # 数据量太少 - continue - - terrain_wins = len(terrain_left_games[terrain_left_games['Result'] == 'L']) + \ - len(terrain_right_games[terrain_right_games['Result'] == 'R']) - terrain_win_rate = terrain_wins / terrain_total - - # 无地形时的表现 - normal_left_games = df[(df[f'FL{terrain_idx+1}'] == 0) & (df[f'L{monster_idx}'] > 0)] - normal_right_games = df[(df[f'FR{terrain_idx+1}'] == 0) & (df[f'R{monster_idx}'] > 0)] - - normal_total = len(normal_left_games) + len(normal_right_games) - if normal_total < 5: - continue - - normal_wins = len(normal_left_games[normal_left_games['Result'] == 'L']) + \ - len(normal_right_games[normal_right_games['Result'] == 'R']) - normal_win_rate = normal_wins / normal_total - - # 计算影响程度 - effect = terrain_win_rate - normal_win_rate - - if abs(effect) >= 0.05: # 胜率差异超过5%才记录 - terrain_effects.append({ - '地形': terrain_name, - '怪物': monster_name, - '怪物ID': monster_idx, - '地形胜率': terrain_win_rate, - '普通胜率': normal_win_rate, - '影响程度': effect, - '地形场次': terrain_total, - '普通场次': normal_total - }) - - # 按影响程度绝对值排序 - terrain_effects.sort(key=lambda x: -abs(x['影响程度'])) - return pd.DataFrame(terrain_effects[:20]) # 增加到前20个 - - -def analyze_device_counter_effects(df): - """分析五个装置对怪物的克制效果""" - device_counter_results = {} - - # 获取实际的地形特征列名 - terrain_feature_columns = get_terrain_feature_columns() - - # 定义五个装置类别及其对应的地形特征 - device_categories = { - 'altar': { - 'name': '祭坛', - 'features': [f for f in terrain_feature_columns if 'altar' in f], - 'description': '祭坛类装置' - }, - 'block': { - 'name': '箱子/阻挡', - 'features': [f for f in terrain_feature_columns if 'block' in f], - 'description': '方块阻挡类装置' - }, - 'coil': { - 'name': '电桩', - 'features': [f for f in terrain_feature_columns if 'coil' in f], - 'description': '线圈电桩装置' - }, - 'crossbow': { - 'name': '弩箭', - 'features': [f for f in terrain_feature_columns if 'crossbow' in f], - 'description': '弩炮装置' - }, - 'fire': { - 'name': '火炮', - 'features': [f for f in terrain_feature_columns if 'fire' in f], - 'description': '火炮装置' - } - } - - for device_key, device_info in device_categories.items(): - device_name = device_info['name'] - device_features = device_info['features'] - - if not device_features: - continue - - device_effects = [] - - # 对每个怪物分析该装置的克制效果 - for monster_idx in range(1, MONSTER_COUNT + 1): - monster_name = get_monster_name(monster_idx) - - # 收集该装置所有特征的统计数据 - total_device_games = 0 - total_device_wins = 0 - total_normal_games = 0 - total_normal_wins = 0 - - for terrain_key in device_features: - if terrain_key not in terrain_feature_columns: - continue - - terrain_idx = terrain_feature_columns.index(terrain_key) - - # 转换数据类型 - df[f'FL{terrain_idx+1}'] = pd.to_numeric(df[f'FL{terrain_idx+1}'], errors='coerce').fillna(0) - df[f'FR{terrain_idx+1}'] = pd.to_numeric(df[f'FR{terrain_idx+1}'], errors='coerce').fillna(0) - df[f'L{monster_idx}'] = pd.to_numeric(df[f'L{monster_idx}'], errors='coerce').fillna(0) - df[f'R{monster_idx}'] = pd.to_numeric(df[f'R{monster_idx}'], errors='coerce').fillna(0) - - # 有该装置时的表现 - device_left_games = df[(df[f'FL{terrain_idx+1}'] == 1) & (df[f'L{monster_idx}'] > 0)] - device_right_games = df[(df[f'FR{terrain_idx+1}'] == 1) & (df[f'R{monster_idx}'] > 0)] - - device_games_count = len(device_left_games) + len(device_right_games) - device_wins_count = len(device_left_games[device_left_games['Result'] == 'L']) + \ - len(device_right_games[device_right_games['Result'] == 'R']) - - # 无该装置时的表现 - normal_left_games = df[(df[f'FL{terrain_idx+1}'] == 0) & (df[f'L{monster_idx}'] > 0)] - normal_right_games = df[(df[f'FR{terrain_idx+1}'] == 0) & (df[f'R{monster_idx}'] > 0)] - - normal_games_count = len(normal_left_games) + len(normal_right_games) - normal_wins_count = len(normal_left_games[normal_left_games['Result'] == 'L']) + \ - len(normal_right_games[normal_right_games['Result'] == 'R']) - - total_device_games += device_games_count - total_device_wins += device_wins_count - total_normal_games += normal_games_count - total_normal_wins += normal_wins_count - - # 计算整体效果 - if total_device_games >= 10 and total_normal_games >= 10: # 确保有足够的数据 - device_win_rate = total_device_wins / total_device_games - normal_win_rate = total_normal_wins / total_normal_games - effect = device_win_rate - normal_win_rate - - # 计算克制程度(负值表示被该装置克制) - counter_effect = -effect # 装置对怪物的克制效果 - - if abs(effect) >= 0.05: # 胜率差异超过5%才记录 - device_effects.append({ - '怪物': monster_name, - '怪物ID': monster_idx, - '装置胜率': device_win_rate, - '普通胜率': normal_win_rate, - '克制程度': counter_effect, # 正值表示被该装置克制 - '装置场次': total_device_games, - '普通场次': total_normal_games, - '效果类型': '被克制' if counter_effect > 0 else '克制装置' - }) - - # 按克制程度排序(被克制程度最高的在前) - device_effects.sort(key=lambda x: -x['克制程度']) - device_counter_results[device_key] = { - 'name': device_name, - 'description': device_info['description'], - 'features': device_features, - 'effects': device_effects[:10] # 取前10个被克制最严重的怪物 - } - - return device_counter_results - - -def create_html_table(df, columns, title, is_combo=False, monster_relations=None): - """创建带有怪物头像的HTML表格""" - html = f"

{title}

\n\n" - - # 表头 - if is_combo: - html += "" - else: - html += "" - - for col in columns: - html += f"" - - # 如果是胜率表且有关系数据,添加额外的列 - if not is_combo and monster_relations and title == '所有怪物胜率排行榜': - html += "" - - html += "\n" - - # 表格内容 - row_number = 1 - for idx, row in df.iterrows(): - html += "" - - # 怪物图片和名称 - monster_id = None - if is_combo and 'ID1' in row and 'ID2' in row: - monster1_name = get_monster_name(row['ID1']) - monster2_name = get_monster_name(row['ID2']) - monster1_orig = get_monster_original_name(row['ID1']) - monster2_orig = get_monster_original_name(row['ID2']) - html += f"""""" - elif '怪物ID' in row: - monster_name = get_monster_name(row['怪物ID']) - monster_orig = get_monster_original_name(row['怪物ID']) - display_name = row.get('怪物', monster_name) - monster_id = row['怪物ID'] - html += f"""""" - else: - # 对于胜率表,使用索引作为怪物名称 - monster_name = idx - # 尝试从怪物数据中获取ID - monster_orig = monster_name - for mid in range(1, MONSTER_COUNT + 1): - if get_monster_name(mid) == monster_name: - monster_id = mid - monster_orig = get_monster_original_name(mid) - break - html += f"""""" - - # 数据列 - for col in columns: - value = row[col] - if isinstance(value, float): - if '率' in col or '程度' in col: - html += f"" - else: - html += f"" - else: - html += f"" - - # 如果是胜率表且有关系数据,添加关系信息 - if not is_combo and monster_relations and title == '所有怪物胜率排行榜' and monster_id: - relations = monster_relations.get(int(monster_id), {}) - - # 最佳队友 - html += "" - - # 克制关系 - html += "" - - # 被克制关系 - html += "" - - html += "\n" - row_number += 1 - - html += "
组合怪物{col}最佳队友克制被克制
- {row_number}. - - {monster1_name}
- - {monster2_name} -
- {row_number}. - - {display_name} - - {row_number}. - - {monster_name} - {value:.2%}{value:.2f}{value}" - if relations and 'best_teammates' in relations and relations['best_teammates']: - teammates = [] - for teammate in relations['best_teammates']: - teammate_orig = get_monster_original_name(teammate['partner_id']) - teammates.append(f"""
- - {teammate['partner_name']} ({teammate['lift']:.2f}x) -
""") - html += "".join(teammates) - else: - html += "暂无数据" - html += "
" - if relations and 'counters' in relations and relations['counters']: - counters = [] - for counter in relations['counters']: - counter_orig = get_monster_original_name(counter['opponent_id']) - counters.append(f"""
- - {counter['opponent_name']} ({counter['win_rate']:.0%}) -
""") - html += "".join(counters) - else: - html += "暂无数据" - html += "
" - if relations and 'countered_by' in relations and relations['countered_by']: - countered = [] - for counter in relations['countered_by']: - counter_orig = get_monster_original_name(counter['opponent_id']) - countered.append(f"""
- - {counter['opponent_name']} ({counter['lose_rate']:.0%}) -
""") - html += "".join(countered) - else: - html += "暂无数据" - html += "
\n" - return html - - -def create_device_counter_html(device_counter_effects): - """创建装置克制效果的HTML表格""" - html = "

装置克制效果统计

\n" - - for device_key, device_data in device_counter_effects.items(): - if not device_data['effects']: - continue - - device_name = device_data['name'] - device_description = device_data['description'] - effects = device_data['effects'] - - html += f"

{device_name}({device_description})

\n" - html += "\n" - html += "" - html += "\n" - - row_number = 1 - for effect in effects: - monster_name = effect['怪物'] - monster_orig = get_monster_original_name(effect['怪物ID']) - html += f""" - - - - - - - - \n""" - row_number += 1 - - html += "
怪物克制程度装置胜率普通胜率装置场次普通场次效果类型
- {row_number}. - - {monster_name} - {effect['克制程度']:.2%}{effect['装置胜率']:.2%}{effect['普通胜率']:.2%}{effect['装置场次']}{effect['普通场次']}{effect['效果类型']}
\n
\n" - - return html - - -def generate_comprehensive_report(): - """生成综合统计报告""" - print("正在加载数据...") - df = load_data() - - print("正在计算怪物胜率...") - win_rates = calculate_all_monster_win_rates(df) - - print("正在分析怪物配合...") - combinations = analyze_monster_combinations(df) - - print("正在分析被克制关系...") - countered = find_countered_monsters(df) - - if FIELD_FEATURE_COUNT > 0: - print("正在分析地形效果...") - terrain_effects = analyze_terrain_effects(df) - - print("正在分析装置克制效果...") - device_counter_effects = analyze_device_counter_effects(df) - else: - print("地形特征数量为0,跳过地形分析...") - terrain_effects = pd.DataFrame() - device_counter_effects = {} - - print("正在分析个体怪物关系...") - monster_relations = analyze_individual_monster_relations(df) - - # 创建HTML报告 - total_battles = len(df) - monster_count = MONSTER_COUNT - field_count = FIELD_FEATURE_COUNT - - html = f""" - - - - - - -

明日方舟争锋频道绿藤城

-
-

数据概览:

-
    -
  • 总战斗记录:{total_battles} 场
  • -
  • 统计怪物数量:{monster_count} 种
  • -
  • 地形特征数量:{field_count} 种
  • -
-
-""" - - # 1. 所有怪物胜率 - if not win_rates.empty: - html += create_html_table(win_rates, ['胜场', '总场数', '胜率', '参战率'], '所有怪物胜率排行榜', monster_relations=monster_relations) - else: - html += "

所有怪物胜率排行榜

暂无数据

" - - # 2. 最佳配合 - if not combinations.empty: - html += create_html_table(combinations.head(20), ['提升度', '组合胜率', '出场次数'], '最佳怪物配合TOP20', is_combo=True) - else: - html += "

最佳怪物配合

暂无足够的配合数据

" - - - # 4. 地形效果 - if not terrain_effects.empty: - html += create_html_table(terrain_effects, ['地形', '地形胜率', '普通胜率', '影响程度'], '地形影响最大的怪物TOP20') - else: - html += "

地形影响

暂无足够的地形数据

" - - # 5. 装置克制效果 - if device_counter_effects: - html += create_device_counter_html(device_counter_effects) - else: - html += "

装置克制效果统计

暂无足够的装置数据

" - - timestamp = pd.Timestamp.now().strftime('%Y-%m-%d %H:%M:%S') - html += f""" -
-

报告生成时间:{timestamp}

-

注:数据基于历史战斗记录,仅供参考

-
- - -""" - - # 保存报告 - with open('comprehensive_monster_report.html', 'w', encoding='utf-8') as f: - f.write(html) - - print("统计报告已生成:comprehensive_monster_report.html") - - # 也输出到控制台 - print("\n=== 怪物胜率TOP10 ===") - print(win_rates.head(10).to_string()) - - if not combinations.empty: - print("\n=== 最佳配合TOP10 ===") - print(combinations.head(10)[['组合', '提升度', '组合胜率', '出场次数']].to_string()) - - # 输出装置克制效果 - if device_counter_effects: - print("\n=== 装置克制效果统计 ===") - for device_key, device_data in device_counter_effects.items(): - if device_data['effects']: - print(f"\n{device_data['name']}({device_data['description']})最克制的怪物TOP5:") - for i, effect in enumerate(device_data['effects'][:5]): - print(f" {i+1}. {effect['怪物']} - 克制程度: {effect['克制程度']:.2%} ({effect['效果类型']})") - - if not terrain_effects.empty: - print("\n=== 地形影响TOP10 ===") - print(terrain_effects.head(10)[['地形', '怪物', '影响程度', '地形胜率', '普通胜率']].to_string()) - - - -if __name__ == "__main__": - generate_comprehensive_report() diff --git "a/\343\200\220\350\204\232\346\234\254\343\200\221\351\223\201\351\262\250\351\261\274\345\220\257\345\212\250\357\274\201(\345\270\246PyTorch\347\211\210\346\234\254\351\200\211\346\213\251\347\211\210).bat" b/iron_shark_launcher.bat similarity index 62% rename from "\343\200\220\350\204\232\346\234\254\343\200\221\351\223\201\351\262\250\351\261\274\345\220\257\345\212\250\357\274\201(\345\270\246PyTorch\347\211\210\346\234\254\351\200\211\346\213\251\347\211\210).bat" rename to iron_shark_launcher.bat index 542044f..b58d5b2 100644 --- "a/\343\200\220\350\204\232\346\234\254\343\200\221\351\223\201\351\262\250\351\261\274\345\220\257\345\212\250\357\274\201(\345\270\246PyTorch\347\211\210\346\234\254\351\200\211\346\213\251\347\211\210).bat" +++ b/iron_shark_launcher.bat @@ -1,73 +1,81 @@ -@echo off -chcp 65001 >nul -title CannotMax -echo 请按任意键启动CannotMax... -pause >nul - -set "current_dir=%cd%" - -:: 检查 uv 是否存在 -where uv >nul 2>nul -if %errorlevel% equ 0 goto run_main - -:: 安装 uv -echo 未检测到 uv,正在安装... -powershell -ExecutionPolicy Bypass -Command "irm https://gitee.com/wangnov/uv-custom/releases/download/latest/uv-installer-custom.ps1 | iex" - -call :refresh_path -:: 验证 uv 是否可用 -where uv >nul 2>nul -if %errorlevel% neq 0 ( - echo 安装 uv 后仍未找到,请检查安装路径 - pause - exit /b 1 -) -:: =================================== - -:run_main -cd /d "%current_dir%" - -:: ===== PyTorch安装选项询问 ===== -set "torch_choice=none" -echo. -echo 是否需要安装PyTorch? (5秒后自动跳过) -echo C/c - CPU版本 -echo G/g - CUDA版本 -echo N/n - 跳过安装 -echo ------------------------------------ - -:: 使用choice命令实现带超时的输入 -choice /c CGN /t 5 /d N /n >nul -if errorlevel 3 ( - set "torch_choice=none" -) else if errorlevel 2 ( - set "torch_choice=cuda" -) else if errorlevel 1 ( - set "torch_choice=cpu" -) - -:: 根据选择安装PyTorch -if "%torch_choice%"=="cpu" ( - echo 安装CPU版本的PyTorch... - uv add torch torchvision --extra cpu -) else if "%torch_choice%"=="cuda" ( - echo 安装CUDA版本的PyTorch... - uv add torch torchvision --extra cu128 -) else ( - echo 跳过PyTorch安装 -) -echo. - -:: =================================== -uv run main.py - -echo 主程序已退出,感谢您的使用! -pause >nul -exit /b - -:: 刷新 PATH 的函数 -:refresh_path -for /f "skip=2 tokens=3*" %%a in ('reg query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v Path 2^>nul') do set "SYSTEM_PATH=%%a %%b" -for /f "skip=2 tokens=3*" %%a in ('reg query "HKCU\Environment" /v Path 2^>nul') do set "USER_PATH=%%a %%b" -set "PATH=%USER_PATH%;%SYSTEM_PATH%" +@echo off +chcp 65001 >nul +title CannotMax +echo 请按任意键启动CannotMax... +pause >nul + +set "current_dir=%cd%" + +:: 检查 uv 是否存在 +where uv >nul 2>nul +if %errorlevel% equ 0 goto run_main + +:: 安装 uv +echo 未检测到 uv,正在安装... +powershell -ExecutionPolicy Bypass -Command "irm https://gitee.com/wangnov/uv-custom/releases/download/latest/uv-installer-custom.ps1 | iex" + +call :refresh_path +:: 验证 uv 是否可用 +where uv >nul 2>nul +if %errorlevel% neq 0 ( + echo 安装 uv 后仍未找到,请检查安装路径 + pause + exit /b 1 +) +:: =================================== + +:run_main +cd /d "%current_dir%" + +:: ===== 选择推理环境选项询问 ===== +set "torch_choice=none" +echo. +echo 选择推理环境? (5秒后自动跳过) +echo C/c - Pytorch CPU版本 +echo D/d - Pytorch CUDA 12.8版本 +echo E/e - Pytorch CUDA 13.0版本 +echo N/n - 使用onnxruntime(默认) +echo ------------------------------------ + +:: 使用choice命令实现带超时的输入 +choice /c CDEN /t 5 /d N /n >nul +:: choice 的 errorlevel 对应顺序为: C=1, D=2, E=3, N=4 +if errorlevel 4 ( + set "torch_choice=none" +) else if errorlevel 3 ( + set "torch_choice=cu130" +) else if errorlevel 2 ( + set "torch_choice=cu128" +) else ( + set "torch_choice=cpu" +) + +:: 根据选择使用对应环境运行主程序 +if "%torch_choice%"=="cpu" ( + echo 使用Pytorch CPU版本... + uv sync --extra cpu +) else if "%torch_choice%"=="cu128" ( + echo 使用Pytorch CUDA 12.8版本... + uv sync --extra cu128 +) else if "%torch_choice%"=="cu130" ( + echo 使用Pytorch CUDA 13.0版本... + uv sync --extra cu130 +) else ( + echo 使用onnxruntime... + uv sync +) +echo. + +:: =================================== +uv run main.py + +echo 主程序已退出,感谢您的使用! +pause >nul +exit /b + +:: 刷新 PATH 的函数 +:refresh_path +for /f "skip=2 tokens=3*" %%a in ('reg query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v Path 2^>nul') do set "SYSTEM_PATH=%%a %%b" +for /f "skip=2 tokens=3*" %%a in ('reg query "HKCU\Environment" /v Path 2^>nul') do set "USER_PATH=%%a %%b" +set "PATH=%USER_PATH%;%SYSTEM_PATH%" exit /b \ No newline at end of file diff --git a/main.py b/main.py index 85a35e4..af13f4c 100644 --- a/main.py +++ b/main.py @@ -8,27 +8,54 @@ import numpy as np from pathlib import Path import onnxruntime # workaround: Pre-import to avoid ImportError: DLL load failed while importing onnxruntime_pybind11_state: 动态链接库(DLL)初始化例程失败。 -from PyQt6.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout -from PyQt6.QtWidgets import QLabel, QPushButton, QLineEdit, QCheckBox, QComboBox, QButtonGroup -from PyQt6.QtWidgets import QGroupBox, QMessageBox, QGraphicsDropShadowEffect, QFrame -from PyQt6.QtCore import Qt, pyqtSignal, QThread, QPropertyAnimation, QEasingCurve +from PyQt6.QtWidgets import ( + QApplication, + QMainWindow, + QWidget, + QVBoxLayout, + QHBoxLayout, +) +from PyQt6.QtWidgets import ( + QLabel, + QPushButton, + QLineEdit, + QCheckBox, + QComboBox, + QButtonGroup, +) +from PyQt6.QtWidgets import ( + QGroupBox, + QMessageBox, + QGraphicsDropShadowEffect, + QFrame, +) +from PyQt6.QtCore import ( + Qt, + pyqtSignal, + QThread, + QPropertyAnimation, + QEasingCurve, +) from PyQt6.QtGui import QPixmap, QFont, QIcon, QPainter, QColor import PyQt6.QtCore as QtCore -import loadData -import auto_fetch -import maa_adb_connector -from maa_adb_connector import AdbConnectorAdapter, ConnectionTypeRegistry, InputMethodRegistry, MaaFrameworkDetector -from dark_mode_style_fix import DarkModeStyleFix -import similar_history_match -import recognize -from recognize import MONSTER_COUNT -from specialmonster import SpecialMonsterHandler -import data_package -import winrt_capture -from config import FIELD_FEATURE_COUNT, MONSTER_DATA -from simular_history_match_ui import HistoryMatchUI -from input_panel_ui import InputPanelUI +from src.data import load_data +from src.game import auto_fetch +from src.game.maa_adb_connector import ( + AdbConnectorAdapter, + ConnectionTypeRegistry, + InputMethodRegistry, +) +from src.ui.dark_mode_style_fix import DarkModeStyleFix +from src.analysis import similar_history_match +from src.recognition import recognize +from src.recognition.recognize import MONSTER_COUNT +from src.recognition.specialmonster import SpecialMonsterHandler +from src.data import data_package +from src.game import winrt_capture +from src.core.config import MONSTER_DATA +from src.ui.similar_history_match_ui import HistoryMatchUI +from src.ui.input_panel_ui import InputPanelUI logging.getLogger().setLevel(logging.DEBUG) logging.getLogger("PIL").setLevel(logging.INFO) @@ -43,19 +70,18 @@ try: - from predict import CannotModel - from train import UnitAwareTransformer + from src.models.predict import CannotModel logger.info("Using PyTorch model for predictions.") except: - from predict_onnx import CannotModel + from src.models.predict_onnx import CannotModel logger.info("Using ONNX model for predictions.") class ADBConnectorThread(QThread): """ - Worker thread to run loadData.AdbConnector.connect() without blocking the UI. + Worker thread to run load_data.AdbConnector.connect() without blocking the UI. """ connect_finished = pyqtSignal() @@ -68,6 +94,7 @@ def run(self): self.app.adb_connector.connect() self.connect_finished.emit() + class ArknightsApp(QMainWindow): # 添加自定义信号 update_button_signal = pyqtSignal(str) # 用于更新按钮文本 @@ -98,9 +125,11 @@ def __init__(self): # 尝试连接模拟器 self.adb_connector = AdbConnectorAdapter() - self.pc_connector = loadData.PcConnector() + self.pc_connector = load_data.PcConnector() self.adb_connector_thread = ADBConnectorThread(self) - self.adb_connector_thread.connect_finished.connect(self.on_adb_connected) + self.adb_connector_thread.connect_finished.connect( + self.on_adb_connected + ) self.adb_connector_thread.start() self.auto_fetch_running = False @@ -119,10 +148,16 @@ def __init__(self): self.history_match = similar_history_match.HistoryMatch() # Ensure feat_past and N_history are initialized try: - self.history_match.feat_past = np.hstack([self.history_match.past_left, self.history_match.past_right]) + self.history_match.feat_past = np.hstack( + [self.history_match.past_left, self.history_match.past_right] + ) except Exception: self.history_match.feat_past = None - self.history_match.N_history = 0 if self.history_match.labels is None else len(self.history_match.labels) + self.history_match.N_history = ( + 0 + if self.history_match.labels is None + else len(self.history_match.labels) + ) logger.info("错题本加载成功") # 初始化特殊怪物语言触发处理程序 @@ -135,7 +170,9 @@ def __init__(self): self.recognize_button.setEnabled(False) self.recognize_button.setToolTip("模型未加载,无法使用此功能") self.input_panel.predict_button.setEnabled(False) - self.input_panel.predict_button.setToolTip("模型未加载,无法使用此功能") + self.input_panel.predict_button.setToolTip( + "模型未加载,无法使用此功能" + ) def init_ui(self): try: @@ -144,13 +181,19 @@ def init_ui(self): version = pyproject_data["project"]["version"] except (FileNotFoundError, KeyError): version = "unknown" - model_name = Path(self.cannot_model.model_path).name if self.cannot_model.model_path else "未加载" - self.setWindowTitle(f"铁鲨鱼_Arknights Neural Network - v{version} - model: {model_name}") - self.setWindowIcon(QIcon("ico/icon.ico")) + model_name = ( + Path(self.cannot_model.model_path).name + if self.cannot_model.model_path + else "未加载" + ) + self.setWindowTitle( + f"铁鲨鱼_Arknights Neural Network - v{version} - model: {model_name}" + ) + self.setWindowIcon(QIcon("src/resources/assets/icons/icon.ico")) self.setGeometry(100, 100, 500, 580) self.setMinimumWidth(580) self.setMaximumWidth(580) - self.background = QPixmap("ico/background.png") + self.background = QPixmap("src/resources/assets/icons/background.png") # 初始化动画对象 self.size_animation = QPropertyAnimation(self, b"size") @@ -177,8 +220,7 @@ def init_ui(self): # 顶部区域 - 输入显示 input_display = QGroupBox() - input_display.setStyleSheet( - """ + input_display.setStyleSheet(""" QGroupBox { background-color: rgba(0, 0, 0, 120); border-radius: 15px; @@ -192,8 +234,7 @@ def init_ui(self): left: 15px; padding: 0 5px; } - """ - ) + """) input_layout = QHBoxLayout(input_display) # 左侧人物显示 @@ -220,15 +261,13 @@ def init_ui(self): # 中部区域 - 预测结果 result_group = QGroupBox() - result_group.setStyleSheet( - """ + result_group.setStyleSheet(""" QGroupBox { background-color: rgba(120, 120, 120, 10); border-radius: 15px; border: 1px solid #747474; } - """ - ) + """) result_layout = QVBoxLayout(result_group) result_layout.setSpacing(10) result_layout.setContentsMargins(10, 10, 10, 10) @@ -240,10 +279,16 @@ def init_ui(self): result_layout.addWidget(self.result_label) # 添加模型名称显示 - model_name = Path(self.cannot_model.model_path).name if self.cannot_model.model_path else "未加载" + model_name = ( + Path(self.cannot_model.model_path).name + if self.cannot_model.model_path + else "未加载" + ) self.model_name_label = QLabel(f"model: {model_name}") self.model_name_label.setFont(QFont("Microsoft YaHei", 8)) - self.model_name_label.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignBottom) + self.model_name_label.setAlignment( + Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignBottom + ) self.model_name_label.setStyleSheet("color: #666666;") result_layout.addWidget(self.model_name_label) @@ -385,7 +430,9 @@ def init_ui(self): self.connection_type_combo = QComboBox() for ct in ConnectionTypeRegistry.get_all_types(): self.connection_type_combo.addItem(ct.display_name, ct.type_id) - self.connection_type_combo.currentIndexChanged.connect(self.on_connection_type_changed) + self.connection_type_combo.currentIndexChanged.connect( + self.on_connection_type_changed + ) self.input_method_label = QLabel("输入方式:") self.input_method_combo = QComboBox() @@ -395,7 +442,9 @@ def init_ui(self): idx = self.input_method_combo.findData(default_method.method_id) if idx >= 0: self.input_method_combo.setCurrentIndex(idx) - self.input_method_combo.currentIndexChanged.connect(self.on_input_method_changed) + self.input_method_combo.currentIndexChanged.connect( + self.on_input_method_changed + ) maa_row_layout.addWidget(self.connection_type_label) maa_row_layout.addWidget(self.connection_type_combo) @@ -531,7 +580,9 @@ def animate_size_change(self, target_width, target_height=None): self.setMaximumWidth(max(self.width(), target_width)) self.size_animation.setStartValue(self.size()) - self.size_animation.setEndValue(QtCore.QSize(target_width, target_height)) + self.size_animation.setEndValue( + QtCore.QSize(target_width, target_height) + ) self.size_animation.start() def set_fixed_after_animation(): @@ -550,14 +601,14 @@ def on_mode_changed(self, mode): self.current_capture_mode = mode logger.info(f"切换捕获模式为: {mode}") - is_win_mode = (mode == "WIN") - is_adb_mode = (mode == "ADB") - is_pc_mode = (mode == "PC") + is_win_mode = mode == "WIN" + is_adb_mode = mode == "ADB" + is_pc_mode = mode == "PC" # 切换窗口捕获相关控件 self.choose_window_button.setEnabled(is_win_mode) self.reselect_button.setEnabled(is_win_mode) - + # 切换 ADB 相关控件 self.serial_label.setEnabled(is_adb_mode) self.serial_entry.setEnabled(is_adb_mode) @@ -578,20 +629,30 @@ def on_mode_changed(self, mode): if self.recognizer._winrt is None: self.choose_capture_window() elif mode == "PC": - self.recognizer = recognize.RecognizeMonster(method="ADB") # reuse ADB reading methodology but on PC Connector + self.recognizer = recognize.RecognizeMonster( + method="ADB" + ) # reuse ADB reading methodology but on PC Connector if not self.pc_connector.is_connected: self.pc_connector.connect() if not self.pc_connector.is_connected: - QMessageBox.warning(self, "警告", "未能连接到PC端窗口(明日方舟)。") + QMessageBox.warning( + self, "警告", "未能连接到PC端窗口(明日方舟)。" + ) def on_adb_connected(self): logger.info("模拟器初始化完成") if self.adb_connector.is_maa_available: self.maa_status_label.setText("MAA Framework已连接") - self.maa_status_label.setStyleSheet("color: #00aa00; font-size: 10px;") + self.maa_status_label.setStyleSheet( + "color: #00aa00; font-size: 10px;" + ) else: - self.maa_status_label.setText("使用自有ADB实现(MAA Framework不可用)") - self.maa_status_label.setStyleSheet("color: #996600; font-size: 10px;") + self.maa_status_label.setText( + "使用自有ADB实现(MAA Framework不可用)" + ) + self.maa_status_label.setStyleSheet( + "color: #996600; font-size: 10px;" + ) def on_connection_type_changed(self, index): type_id = self.connection_type_combo.currentData() @@ -605,7 +666,9 @@ def on_connection_type_changed(self, index): if self.adb_connector.is_connected: self.adb_connector.disconnect() self.maa_status_label.setText("已断开,请重新连接") - self.maa_status_label.setStyleSheet("color: #aa0000; font-size: 10px;") + self.maa_status_label.setStyleSheet( + "color: #aa0000; font-size: 10px;" + ) def on_input_method_changed(self, index): method_id = self.input_method_combo.currentData() @@ -613,7 +676,9 @@ def on_input_method_changed(self, index): return self.adb_connector.set_input_method(method_id) if self.adb_connector.is_connected: - QMessageBox.information(self, "提示", "输入方式已更改,请重新连接以生效") + QMessageBox.information( + self, "提示", "输入方式已更改,请重新连接以生效" + ) def choose_capture_window(self): """弹出窗口选择器,切换 WinRT 截屏源(窗口标题或整屏)。""" @@ -637,20 +702,32 @@ def choose_capture_window(self): return hint = "" if "window_name" in sel: - self.recognizer = recognize.RecognizeMonster(method="WIN", window_name=sel["window_name"], monitor_index=None) + self.recognizer = recognize.RecognizeMonster( + method="WIN", + window_name=sel["window_name"], + monitor_index=None, + ) hint = f"已切换至窗口:{sel['window_name']}" else: idx = max(1, sel["monitor_index"]) - self.recognizer = recognize.RecognizeMonster(method="WIN", window_name=None, monitor_index=idx) + self.recognizer = recognize.RecognizeMonster( + method="WIN", window_name=None, monitor_index=idx + ) hint = f"已切换至整屏:显示器 {sel['monitor_index']}" self.no_region = True - QMessageBox.information(self, "成功", hint + "\n建议重新选择范围。") + QMessageBox.information( + self, "成功", hint + "\n建议重新选择范围。" + ) except Exception as e: - QMessageBox.critical(self, "异常", f"{e}\n\n{traceback.format_exc()}") + QMessageBox.critical( + self, "异常", f"{e}\n\n{traceback.format_exc()}" + ) finally: self._switching_source = False - self.choose_window_button.setEnabled(self.current_capture_mode == "WIN") + self.choose_window_button.setEnabled( + self.current_capture_mode == "WIN" + ) def paintEvent(self, event): painter = QPainter(self) @@ -668,7 +745,9 @@ def paintEvent(self, event): ) def update_input_display(self): - left_monsters_dict, right_monsters_dict = self.input_panel.get_monster_counts() + left_monsters_dict, right_monsters_dict = ( + self.input_panel.get_monster_counts() + ) def update_input_display_half(input_layout, monsters_dict): # 清除现有显示 @@ -681,7 +760,9 @@ def update_input_display_half(input_layout, monsters_dict): value = monsters_dict[str(i)].text() if value.isdigit() and int(value) > 0: has_input = True - monster_widget = self.create_monster_display_widget(i, value) + monster_widget = self.create_monster_display_widget( + i, value + ) input_layout.addWidget(monster_widget) # 如果没有输入,显示提示 if not has_input: @@ -700,13 +781,11 @@ def create_monster_display_widget(self, monster_id, count): shadow.setOffset(2) # 偏移量(0表示均匀四周发光) widget.setGraphicsEffect(shadow) - widget.setStyleSheet( - """ + widget.setStyleSheet(""" QWidget { border-radius: 0px; } - """ - ) + """) layout = QVBoxLayout(widget) layout.setSpacing(2) @@ -719,10 +798,15 @@ def create_monster_display_widget(self, monster_id, count): img_label.setAlignment(Qt.AlignmentFlag.AlignCenter) try: - pixmap = QPixmap(f"images/{MONSTER_DATA['原始名称'][monster_id]}.png") + pixmap = QPixmap( + f"src/resources/assets/images/{MONSTER_DATA['原始名称'][monster_id]}.png" + ) if not pixmap.isNull(): pixmap = pixmap.scaled( - 70, 70, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation + 70, + 70, + Qt.AspectRatioMode.KeepAspectRatio, + Qt.TransformationMode.SmoothTransformation, ) img_label.setPixmap(pixmap) except Exception as e: @@ -740,15 +824,13 @@ def create_monster_display_widget(self, monster_id, count): # 数量标签 count_label = QLabel(count) count_label.setAlignment(Qt.AlignmentFlag.AlignCenter) - count_label.setStyleSheet( - """ + count_label.setStyleSheet(""" color: #EDEDED; font: bold 20px SimHei; border-radius: 5px; padding: 2px 5px; min-width: 20px; - """ - ) + """) layout.addWidget(img_label) layout.addWidget(count_label) @@ -762,28 +844,40 @@ def reset_entries(self): def get_prediction(self): try: - left_monsters_dict, right_monsters_dict = self.input_panel.get_monster_counts() + left_monsters_dict, right_monsters_dict = ( + self.input_panel.get_monster_counts() + ) left_counts = np.zeros(MONSTER_COUNT, dtype=np.int16) right_counts = np.zeros(MONSTER_COUNT, dtype=np.int16) for name, entry in left_monsters_dict.items(): value = entry.text() - left_counts[int(name) - 1] = int(value) if value.isdigit() else 0 + left_counts[int(name) - 1] = ( + int(value) if value.isdigit() else 0 + ) for name, entry in right_monsters_dict.items(): value = entry.text() - right_counts[int(name) - 1] = int(value) if value.isdigit() else 0 + right_counts[int(name) - 1] = ( + int(value) if value.isdigit() else 0 + ) # 构建包含地形的完整特征向量 - full_features = self.input_panel.build_terrain_features(left_counts, right_counts) + full_features = self.input_panel.build_terrain_features( + left_counts, right_counts + ) - prediction = self.cannot_model.get_prediction_with_terrain(full_features) + prediction = self.cannot_model.get_prediction_with_terrain( + full_features + ) return prediction except FileNotFoundError: QMessageBox.critical(self, "错误", "未找到模型文件,请先训练") except RuntimeError as e: if "size mismatch" in str(e): - QMessageBox.critical(self, "错误", "模型结构不匹配!请删除旧模型并重新训练") + QMessageBox.critical( + self, "错误", "模型结构不匹配!请删除旧模型并重新训练" + ) else: QMessageBox.critical(self, "错误", f"模型加载失败: {str(e)}") except ValueError: @@ -810,13 +904,20 @@ def update_prediction(self, prediction): else: self.result_label.setStyleSheet("color: #25ace2; font: bold,14px;") - left_monsters_dict, right_monsters_dict = self.input_panel.get_monster_counts() + left_monsters_dict, right_monsters_dict = ( + self.input_panel.get_monster_counts() + ) # 生成结果文本 if winner != "难说": - result_text = f"预测胜方: {winner}\n" f"左 {left_win_prob:.2%} | 右 {right_win_prob:.2%}\n" + result_text = ( + f"预测胜方: {winner}\n" + f"左 {left_win_prob:.2%} | 右 {right_win_prob:.2%}\n" + ) else: result_text = ( - f"这一把{winner}\n" f"左 {left_win_prob:.2%} | 右 {right_win_prob:.2%}\n" f"难道说?难道说?难道说?\n" + f"这一把{winner}\n" + f"左 {left_win_prob:.2%} | 右 {right_win_prob:.2%}\n" + f"难道说?难道说?难道说?\n" ) self.result_label.setStyleSheet("color: black; font: bold,24px;") @@ -835,8 +936,12 @@ def predict(self): self.update_input_display() if self.history_match_ui.isVisible(): - left_monsters_dict, right_monsters_dict = self.input_panel.get_monster_counts() - self.history_match_ui.render_similar_matches(left_monsters_dict, right_monsters_dict) + left_monsters_dict, right_monsters_dict = ( + self.input_panel.get_monster_counts() + ) + self.history_match_ui.render_similar_matches( + left_monsters_dict, right_monsters_dict + ) def get_recognize(self): """ @@ -851,7 +956,7 @@ def get_recognize(self): screenshot = self.active_connector.capture_screenshot() if screenshot is None: logger.error(f"{self.current_capture_mode} 截图失败") - + results = self.recognizer.process_regions(screenshot) else: # WIN 模式,recognizer 内部处理 WinRT 或 PIL @@ -888,22 +993,32 @@ def recognize_and_predict(self): self.update_prediction(prediction) # 历史对局 if self.history_match_ui.isVisible(): - left_monsters_dict, right_monsters_dict = self.input_panel.get_monster_counts() - self.history_match_ui.render_similar_matches(left_monsters_dict, right_monsters_dict) + left_monsters_dict, right_monsters_dict = ( + self.input_panel.get_monster_counts() + ) + self.history_match_ui.render_similar_matches( + left_monsters_dict, right_monsters_dict + ) def toggle_history_panel(self): """切换历史对局面板的显示""" target_width = self.width() if self.history_match is None: - QMessageBox.warning(self, "警告", "历史数据加载失败,无法显示历史对局") + QMessageBox.warning( + self, "警告", "历史数据加载失败,无法显示历史对局" + ) return is_visible = self.history_match_ui.isVisible() self.history_match_ui.setVisible(not is_visible) if not is_visible: self.history_button.setText("隐藏历史对局") - left_monsters_dict, right_monsters_dict = self.input_panel.get_monster_counts() - self.history_match_ui.render_similar_matches(left_monsters_dict, right_monsters_dict) + left_monsters_dict, right_monsters_dict = ( + self.input_panel.get_monster_counts() + ) + self.history_match_ui.render_similar_matches( + left_monsters_dict, right_monsters_dict + ) target_width += 540 else: self.history_button.setText("显示历史对局") @@ -914,7 +1029,9 @@ def reselect_roi(self): self.recognizer.select_roi() def toggle_auto_fetch(self): - if not (hasattr(self, "auto_fetch") and self.auto_fetch.auto_fetch_running): + if not ( + hasattr(self, "auto_fetch") and self.auto_fetch.auto_fetch_running + ): self.auto_fetch = auto_fetch.AutoFetch( self.active_connector, self.game_mode, @@ -924,7 +1041,8 @@ def toggle_auto_fetch(self): updater=self.update_statistics_callback, start_callback=self.start_callback, stop_callback=self.stop_callback, - training_duration=float(self.duration_entry.text()) * 3600, # 获取训练时长 + training_duration=float(self.duration_entry.text()) + * 3600, # 获取训练时长 recognizer=self.recognizer, cannot_model=self.cannot_model, ) @@ -933,7 +1051,11 @@ def toggle_auto_fetch(self): self.auto_fetch.stop_auto_fetch() def update_statistics(self): - elapsed_time = time.time() - self.auto_fetch.start_time if self.auto_fetch.start_time else 0 + elapsed_time = ( + time.time() - self.auto_fetch.start_time + if self.auto_fetch.start_time + else 0 + ) hours, remainder = divmod(elapsed_time, 3600) minutes, _ = divmod(remainder, 60) stats_text = ( @@ -956,14 +1078,18 @@ def refresh_device_list(self): self.serial_entry.setCurrentIndex(0) else: self.serial_entry.addItem("127.0.0.1:5555") - self.serial_entry.setCurrentText(current_text if current_text else "127.0.0.1:5555") + self.serial_entry.setCurrentText( + current_text if current_text else "127.0.0.1:5555" + ) def update_device_serial(self): new_serial = self.serial_entry.currentText() device_serial = self.adb_connector.update_device_serial(new_serial) self.adb_connector.connect() # 尝试连接新设备 self.serial_entry.setCurrentText(device_serial) - QMessageBox.information(self, "提示", f"已更新模拟器序列号为: {device_serial}") + QMessageBox.information( + self, "提示", f"已更新模拟器序列号为: {device_serial}" + ) def start_callback(self): self.update_button_signal.emit("停止自动获取数据") @@ -987,7 +1113,9 @@ def run_simulation(self): left_monsters_data = {} right_monsters_data = {} - left_monsters_dict, right_monsters_dict = self.input_panel.get_monster_counts() + left_monsters_dict, right_monsters_dict = ( + self.input_panel.get_monster_counts() + ) # 获取左侧怪物信息 for monster_id, entry in left_monsters_dict.items(): @@ -1003,7 +1131,9 @@ def run_simulation(self): except ValueError: logger.error(f"Invalid monster ID: {monster_id}") except Exception as e: - logger.error(f"Error getting monster name for ID {monster_id}: {e}") + logger.error( + f"Error getting monster name for ID {monster_id}: {e}" + ) # 获取右侧怪物信息 for monster_id, entry in right_monsters_dict.items(): @@ -1015,13 +1145,20 @@ def run_simulation(self): if monster_name: right_monsters_data[monster_name] = int(count) else: - logger.error(f"Monster name not found for ID {monster_id}") + logger.error( + f"Monster name not found for ID {monster_id}" + ) except ValueError: logger.error(f"Invalid monster ID: {monster_id}") except Exception as e: - logger.error(f"Error getting monster name for ID {monster_id}: {e}") + logger.error( + f"Error getting monster name for ID {monster_id}: {e}" + ) - simulation_data = {"left": left_monsters_data, "right": right_monsters_data} + simulation_data = { + "left": left_monsters_data, + "right": right_monsters_data, + } json_data = json.dumps(simulation_data, ensure_ascii=False) logger.info(f"Simulation data JSON: {json_data}") @@ -1030,29 +1167,40 @@ def run_simulation(self): # 启动main_sim.py子进程 (非阻塞) # Use sys.executable to ensure the same Python interpreter is used process = subprocess.Popen( - [sys.executable, "main_sim.py"], stdin=subprocess.PIPE, text=True, encoding="utf-8" + [sys.executable, "main_sim.py"], + stdin=subprocess.PIPE, + text=True, + encoding="utf-8", ) # 通过stdin传递JSON数据并关闭stdin process.stdin.write(json_data) process.stdin.close() except FileNotFoundError: - QMessageBox.critical(self, "错误", "未找到 main_sim.py 文件,请检查路径。") + QMessageBox.critical( + self, "错误", "未找到 main_sim.py 文件,请检查路径。" + ) except Exception as e: - QMessageBox.critical(self, "错误", f"启动模拟器时发生错误: {str(e)}") + QMessageBox.critical( + self, "错误", f"启动模拟器时发生错误: {str(e)}" + ) def get_monster_name_by_id(self, monster_id: int): """根据怪物ID获取怪物名称""" - # Need to import MONSTER_MAPPING from simulator.utils + # Need to import MONSTER_MAPPING from src.simulation.utils try: - from simulator.utils import MONSTER_MAPPING + from src.simulation.utils import MONSTER_MAPPING # Adjust for 1-based UI IDs vs 0-based mapping keys monster_name = MONSTER_MAPPING.get(monster_id - 1) if not monster_name: - logger.error(f"Monster ID {monster_id} not found in MONSTER_MAPPING.") + logger.error( + f"Monster ID {monster_id} not found in MONSTER_MAPPING." + ) return monster_name except ImportError: - logger.error("Error importing MONSTER_MAPPING from simulator.utils") + logger.error( + "Error importing MONSTER_MAPPING from src.simulation.utils" + ) return None def update_game_mode(self, mode): @@ -1071,7 +1219,9 @@ def update_stats(self, total, incorrect, duration): def update_image_display(self, qimage): self.image_display.setPixmap( QPixmap.fromImage(qimage).scaled( - self.image_display.width(), self.image_display.height(), Qt.AspectRatioMode.KeepAspectRatio + self.image_display.width(), + self.image_display.height(), + Qt.AspectRatioMode.KeepAspectRatio, ) ) @@ -1081,21 +1231,28 @@ def package_data_and_show(self): if zip_filename: # 在文件浏览器中高亮显示文件 subprocess.run(f'explorer /select,"{zip_filename}"') - QMessageBox.information(self, "成功", f"数据已打包到 {zip_filename}") + QMessageBox.information( + self, "成功", f"数据已打包到 {zip_filename}" + ) else: - QMessageBox.warning(self, "警告", "没有找到可以打包的数据目录。") + QMessageBox.warning( + self, "警告", "没有找到可以打包的数据目录。" + ) except Exception as e: QMessageBox.critical(self, "错误", f"打包数据时发生错误: {str(e)}") - def toggle_always_on_top(self): if self.windowFlags() & Qt.WindowType.WindowStaysOnTopHint: - self.setWindowFlags(self.windowFlags() & ~Qt.WindowType.WindowStaysOnTopHint) + self.setWindowFlags( + self.windowFlags() & ~Qt.WindowType.WindowStaysOnTopHint + ) self.always_on_top_button.setText("窗口置顶") else: - self.setWindowFlags(self.windowFlags() | Qt.WindowType.WindowStaysOnTopHint) + self.setWindowFlags( + self.windowFlags() | Qt.WindowType.WindowStaysOnTopHint + ) self.always_on_top_button.setText("取消置顶") - self.show() # Reapply window flags + self.show() # Reapply window flags def closeEvent(self, event): """窗口关闭时的处理""" diff --git a/main.spec b/main.spec index 05b2505..924881c 100644 --- a/main.spec +++ b/main.spec @@ -11,6 +11,8 @@ a_main = Analysis( pathex=[project_root], binaries=[], datas=[ + ('src', 'src'), + ('vendor', 'vendor'), ('.venv/Lib/site-packages/rapidocr/default_models.yaml', 'rapidocr'), ('.venv/Lib/site-packages/rapidocr/config.yaml', 'rapidocr'), ('.venv/Lib/site-packages/rapidocr/models', 'rapidocr/models') @@ -19,7 +21,7 @@ a_main = Analysis( hookspath=[], hooksconfig={}, runtime_hooks=[], - excludes=['train', 'torch', 'torchvision', 'matplotlib', 'sklearn', 'scikit-learn', 'scipy', 'PyQt6.QtPdf', 'PyQt6.QtNetwork', 'predict', 'onnxscript'], + excludes=['torch', 'torchvision', 'matplotlib', 'sklearn', 'scikit-learn', 'scipy', 'PyQt6.QtPdf', 'PyQt6.QtNetwork', 'predict', 'onnxscript'], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher, @@ -40,7 +42,7 @@ exe_main = EXE( a_main.scripts, [], exclude_binaries=True, - name='main', + name='CannotMax', debug=False, bootloader_ignore_signals=False, strip=False, @@ -51,7 +53,7 @@ exe_main = EXE( target_arch=None, codesign_identity=None, entitlements_file=None, - icon=['ico\\icon_64x64.ico'], + icon=['src\\resources\\assets\\icons\\icon_64x64.ico'], ) # 多开管理器分析 @@ -60,6 +62,8 @@ a_multi = Analysis( pathex=[project_root], binaries=[], datas=[ + ('src', 'src'), + ('vendor', 'vendor'), ('.venv/Lib/site-packages/rapidocr/default_models.yaml', 'rapidocr'), ('.venv/Lib/site-packages/rapidocr/config.yaml', 'rapidocr'), ('.venv/Lib/site-packages/rapidocr/models', 'rapidocr/models') @@ -68,7 +72,7 @@ a_multi = Analysis( hookspath=[], hooksconfig={}, runtime_hooks=[], - excludes=['train', 'torch', 'torchvision', 'matplotlib', 'sklearn', 'scikit-learn', 'scipy', 'PyQt6.QtPdf', 'PyQt6.QtNetwork', 'predict', 'onnxscript'], + excludes=['torch', 'torchvision', 'matplotlib', 'sklearn', 'scikit-learn', 'scipy', 'PyQt6.QtPdf', 'PyQt6.QtNetwork', 'predict', 'onnxscript'], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher, @@ -96,7 +100,7 @@ exe_multi = EXE( target_arch=None, codesign_identity=None, entitlements_file=None, - icon=['ico\\icon_64x64.ico'], + icon=['src\\resources\\assets\\icons\\icon_64x64.ico'], ) coll = COLLECT( diff --git a/main_old.py b/main_old.py deleted file mode 100644 index 89d18ed..0000000 --- a/main_old.py +++ /dev/null @@ -1,650 +0,0 @@ -import logging -import subprocess -import threading -import time -import tkinter as tk -from tkinter import messagebox -import numpy as np -import math -from PIL import Image, ImageTk -from predict import CannotModel -import loadData -import recognize -from train import UnitAwareTransformer -from recognize import MONSTER_COUNT -from similar_history_match import HistoryMatch -from auto_fetch import AutoFetch - -logging.getLogger().setLevel(logging.DEBUG) -logging.getLogger("PIL").setLevel(logging.INFO) -stream_handler = logging.StreamHandler() -formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s",) -stream_handler.setFormatter(formatter) -logging.getLogger().addHandler(stream_handler) -logger = logging.getLogger(__name__) -logger.setLevel(logging.DEBUG) - - -class ArknightsApp: - def __init__(self, root): - self.root = root - self.root.title("Arknights Neural Network") - self.history_match = HistoryMatch() - - self.main_panel = tk.Frame(self.root) - self.main_panel.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) - - # 鼠标滚轮滚动历史面板 - self.root.bind_all("", self._on_mousewheel) - self.root.bind_all("", self._on_shift_mousewheel) - # 运行 - self.no_region = True - self.first_recognize = True - - # 尝试连接模拟器 - self.adb_connector = loadData.AdbConnector() - self.adb_connector.connect() - - # 用户选项 - self.is_invest = tk.BooleanVar(value=False) # 添加投资状态变量 - self.game_mode = tk.StringVar(value="单人") # 添加游戏模式变量,默认单人模式 - self.device_serial = tk.StringVar(value="127.0.0.1:5555") # 添加设备序列号变量 - - # 数据缓存 - self.left_monsters = {} - self.right_monsters = {} - self.images = {} - self.progress_var = tk.StringVar() - - self.load_images() - self.create_widgets() - - # 怪物识别模块 - self.recognizer = recognize.RecognizeMonster() - - # 历史对局面板 - self.history_visible = False - self.history_container = tk.Frame(self.root, bd=1, relief="sunken") - - # Canvas & Scrollbars - self.history_canvas = tk.Canvas(self.history_container, bg="white") - self.history_vscroll = tk.Scrollbar( - self.history_container, orient="vertical", command=self.history_canvas.yview - ) - self.history_hscroll = tk.Scrollbar( - self.history_container, orient="horizontal", command=self.history_canvas.xview - ) - - self.history_canvas.configure( - yscrollcommand=self.history_vscroll.set, xscrollcommand=self.history_hscroll.set - ) - - # 真正放内容的 Frame - self.history_frame = tk.Frame(self.history_canvas) - self.history_canvas.create_window((0, 0), window=self.history_frame, anchor="nw") - - # 更新 scroll region - self.history_frame.bind( - "", - lambda e: self.history_canvas.configure(scrollregion=self.history_canvas.bbox("all")), - ) - - # Canvas + 两条滚动条在 history_container 里排版 - self.history_canvas.grid(row=0, column=0, sticky="nsew") - self.history_vscroll.grid(row=0, column=1, sticky="ns") - self.history_hscroll.grid(row=1, column=0, sticky="ew") - - # 让 Canvas 单元格可伸缩 - self.history_container.grid_rowconfigure(0, weight=1) - self.history_container.grid_columnconfigure(0, weight=1) - - # 模型相关属性 - self.cannot_model = CannotModel() - - - def _on_mousewheel(self, event): - """滑动鼠标滚轮 → 垂直滚动错题本面板""" - self.history_canvas.yview_scroll(int(-1 * (event.delta / 120)), "units") - - def _on_shift_mousewheel(self, event): - """按住 Shift + 滚轮 → 水平滚动错题本面板""" - self.history_canvas.xview_scroll(int(-1 * (event.delta / 120)), "units") - - def load_images(self): - # 获取系统缩放因子 - scaling_factor = self.root.tk.call("tk", "scaling") - base_size = 30 - icon_size = int(base_size * scaling_factor) # 动态计算图标大小 - - for i in range(1, MONSTER_COUNT + 1): - # 使用PIL打开图像并缩放 - img = Image.open(f"images/{i}.png") - width, height = img.size - - # 计算缩放比例,保持宽高比且不超过目标尺寸 - ratio = min(icon_size / width, icon_size / height) - new_size = (int(width * ratio), int(height * ratio)) - - # 高质量缩放 - img_resized = img.resize(new_size, Image.Resampling.LANCZOS) - - # 转换为Tkinter兼容格式 - photo_img = ImageTk.PhotoImage(img_resized) - self.images[str(i)] = photo_img - - def create_widgets(self): - # 创建顶层容器 - self.top_container = tk.Frame(self.main_panel) - self.bottom_container = tk.Frame(self.main_panel) - - # 顶部容器布局(填充整个水平空间) - self.top_container.pack(side=tk.TOP, fill=tk.X, pady=10) - self.bottom_container.pack(side=tk.TOP, fill=tk.BOTH, expand=True, pady=10) - - # 创建居中容器用于放置左右怪物框 - self.monster_center = tk.Frame(self.top_container) - self.monster_center.pack(side=tk.TOP, anchor="center") - - # 创建左右怪物容器(添加边框和背景色) - self.left_frame = tk.Frame( - self.monster_center, borderwidth=2, relief="groove", padx=5, pady=5 - ) - self.right_frame = tk.Frame( - self.monster_center, borderwidth=2, relief="groove", padx=5, pady=5 - ) - - # 添加左右标题 - tk.Label(self.left_frame, text="左侧怪物", font=("Helvetica", 10, "bold")).grid( - row=0, columnspan=10 - ) - tk.Label(self.right_frame, text="右侧怪物", font=("Helvetica", 10, "bold")).grid( - row=0, columnspan=10 - ) - - # 左右布局(添加显式间距并居中) - self.left_frame.pack(side=tk.LEFT, padx=10, anchor="n", pady=5) - self.right_frame.pack(side=tk.RIGHT, padx=10, anchor="n", pady=5) - - for side, frame, monsters in [ - ("left", self.left_frame, self.left_monsters), - ("right", self.right_frame, self.right_monsters), - ]: - row_n = 6 - monsters_per_row = math.ceil(MONSTER_COUNT / row_n) - for row in range(row_n): - start = row * monsters_per_row + 1 - end = min((row + 1) * monsters_per_row + 1, MONSTER_COUNT + 1) - for i in range(start, end): - # 图片标签(缩小尺寸) - tk.Label(frame, image=self.images[str(i)], padx=1, pady=1).grid( - row=row * 2, column=i - start, sticky="ew" - ) - # 输入框(保持宽度5) - monsters[str(i)] = tk.Entry(frame, width=5) - monsters[str(i)].grid( - row=row * 2 + 1, column=i - start, pady=(0, 1) # 减小底部间距 - ) - - # 调整列权重使布局更紧凑 - for col in range(monsters_per_row): - frame.grid_columnconfigure(col, weight=1, minsize=25) # 适当调整最小列宽 - - # 结果显示区域(增加边框) - self.result_frame = tk.Frame(self.bottom_container, relief="ridge", borderwidth=1) - self.result_frame.pack(fill=tk.X, pady=5) - - # 使用更醒目的字体 - self.result_label = tk.Label( - self.result_frame, text="Prediction: ", font=("Helvetica", 16, "bold"), fg="blue" - ) - self.result_label.pack(pady=3) - self.stats_label = tk.Label(self.result_frame, text="", font=("Helvetica", 12), fg="green") - self.stats_label.pack(pady=3) - - # 按钮区域容器(增加边框和背景) - self.button_frame = tk.Frame( - self.bottom_container, relief="groove", borderwidth=2, padx=10, pady=10 - ) - self.button_frame.pack(fill=tk.BOTH, expand=True) - - # 按钮布局(分左右两列布局) - left_buttons = tk.Frame(self.button_frame) - center_buttons = tk.Frame(self.button_frame) # 新增中间按钮容器 - right_buttons = tk.Frame(self.button_frame) - - # 使用grid布局实现均匀分布 - left_buttons.grid(row=0, column=0, sticky="ew") - center_buttons.grid(row=0, column=1, sticky="ew") # 中间列 - right_buttons.grid(row=0, column=2, sticky="ew") - self.button_frame.grid_columnconfigure((0, 1, 2), weight=1) # 均匀分布三列 - - # 左侧按钮列(控制选项) - control_col = tk.Frame(left_buttons) - control_col.pack(anchor="center", expand=True) - - # 时长输入组 - duration_frame = tk.Frame(control_col) - duration_frame.pack(pady=2) - tk.Label(duration_frame, text="训练时长:").pack(side=tk.LEFT) - self.duration_entry = tk.Entry(duration_frame, width=6) - self.duration_entry.insert(0, "-1") - self.duration_entry.pack(side=tk.LEFT, padx=5) - - # 模式选择组 - mode_frame = tk.Frame(control_col) - mode_frame.pack(pady=2) - self.mode_menu = tk.OptionMenu(mode_frame, self.game_mode, "单人", "30人") - self.mode_menu.pack(side=tk.LEFT) - self.invest_checkbox = tk.Checkbutton(mode_frame, text="投资", variable=self.is_invest) - self.invest_checkbox.pack(side=tk.LEFT, padx=5) - - # 中间按钮列(核心操作) - action_col = tk.Frame(center_buttons) - action_col.pack(anchor="center", expand=True) - - # 核心操作按钮 - action_buttons = [("自动获取数据", self.toggle_auto_fetch)] - # 单独处理自动获取数据按钮 - for text, cmd in action_buttons: - btn = tk.Button(action_col, text=text, command=cmd, width=14) # 加宽按钮 - btn.pack(pady=5, ipadx=5) - if text == "自动获取数据": - self.auto_fetch_button = btn - btn.pack(pady=5, ipadx=5) - - # 右侧按钮列(功能按钮) - func_col = tk.Frame(right_buttons) - func_col.pack(anchor="center", expand=True) - - # 预测功能组 - predict_frame = tk.Frame(func_col) - predict_frame.pack(pady=2) - self.predict_button = tk.Button( - predict_frame, text="预测", command=self.predict, width=8, bg="#FFE4B5" - ) - self.predict_button.pack(side=tk.LEFT, padx=2) - - self.recognize_button = tk.Button( - predict_frame, text="识别并预测", command=self.recognize_and_predict, width=10, bg="#98FB98" - ) - self.recognize_button.pack(side=tk.LEFT, padx=2) - - self.reset_button = tk.Button( - predict_frame, text="归零", command=self.reset_entries, width=6 - ) - self.reset_button.pack(side=tk.LEFT, padx=2) - - # 设备序列号组(独立行) - serial_frame = tk.Frame(func_col) - serial_frame.pack(pady=5) - - self.reselect_button = tk.Button( - serial_frame, text="选择范围", command=self.reselect_roi, width=10 - ) - self.reselect_button.pack(side=tk.LEFT) - - tk.Label(serial_frame, text="设备号:").pack(side=tk.LEFT) - self.serial_entry = tk.Entry(serial_frame, textvariable=self.device_serial, width=15) - self.serial_entry.pack(side=tk.LEFT, padx=3) - - self.serial_button = tk.Button( - serial_frame, text="更新", command=self.update_device_serial, width=6 - ) - self.serial_button.pack(side=tk.LEFT) - - # 错题本开关 - self.history_button = tk.Button( - func_col, text="显示错题本", command=self.toggle_history_panel, width=10 - ) - self.history_button.pack(pady=4) # 可以 side=tk.TOP / BOTTOM 都行 - - def toggle_history_panel(self): - if not self.history_visible: - self.history_container.pack(side="right", fill="both", padx=5, pady=5) - self.history_button.config(text="隐藏错题本") - for w in self.history_frame.winfo_children(): - w.destroy() - self.render_history(self.history_frame) - self.history_canvas.configure(scrollregion=self.history_canvas.bbox("all")) - else: - self.history_container.pack_forget() - self.history_button.config(text="显示错题本") - self.history_visible = not self.history_visible - - def render_history(self, parent): - # 准备输入数据(完全匹配ArknightsDataset的处理方式) - left_counts = np.zeros(MONSTER_COUNT, dtype=np.int16) - right_counts = np.zeros(MONSTER_COUNT, dtype=np.int16) - # 从界面获取数据(空值处理为0) - for name, entry in self.left_monsters.items(): - value = entry.get() - left_counts[int(name) - 1] = int(value) if value.isdigit() else 0 - for name, entry in self.right_monsters.items(): - value = entry.get() - right_counts[int(name) - 1] = int(value) if value.isdigit() else 0 - - self.history_match.render_similar_matches(left_counts, right_counts) - try: - left_rate = self.history_match.left_rate - right_rate = self.history_match.right_rate - cur_left = self.history_match.cur_left - cur_right = self.history_match.cur_right - sims = self.history_match.sims - swap = self.history_match.swap - top20_idx = self.history_match.top20_idx - # 清空旧内容 - for w in parent.winfo_children(): - w.destroy() - - # 标题 - head = tk.Frame(parent) - head.pack(fill="x", pady=4) - fgL, fgR = ("#E23F25", "#666") if left_rate > right_rate else ("#666", "#25ace2") - tk.Label(head, text="近5条左右胜率:", font=("Helvetica", 12, "bold")).pack(side="left") - tk.Label( - head, text=f"左边 {left_rate:.2%} ", fg=fgL, font=("Helvetica", 12, "bold") - ).pack(side="left") - tk.Label( - head, text=f"右边 {right_rate:.2%}", fg=fgR, font=("Helvetica", 12, "bold") - ).pack(side="left") - - # 错题本主体渲染 - self._history_parent = parent - self._top20 = top20_idx.tolist() - self._sims = sims - self._swap = swap - self._batch_idx = 0 - - # 调整Canvas宽度 - self.history_canvas.config(width=700) # 增加Canvas宽度 - self.history_frame.config(width=700) # 增加Frame宽度 - - parent.after(0, lambda: self._render_batch(batch_size=5)) - - except Exception as e: - print("[渲染错题本失败]", e) - - def _render_batch(self, batch_size=5): - start = self._batch_idx * batch_size - end = start + batch_size - history_match = self.history_match - parent = self._history_parent - top20 = self._top20 - sims = self._sims - swap = self._swap - - for rank, idx in enumerate(top20[start:end], start + 1): - sims_val = sims[idx] - swapped = swap[idx] - Lh, Rh = (history_match.past_left if not swapped else history_match.past_right)[idx], ( - history_match.past_right if not swapped else history_match.past_left - )[idx] - lab = history_match.labels[idx] - if swapped: - lab = "L" if lab == "R" else "R" - winL, winR = (lab == "L"), (lab == "R") - - # csv中的行数=局数 - real_no = idx + 2 - - row = tk.Frame(parent, pady=6) - row.pack(fill="x") - - # 左侧信息区域 - info_frame = tk.Frame(row) - info_frame.pack(side="left", fill="y", padx=5) - - # 局数 - tk.Label( - info_frame, - text=f"第 {real_no} 局", - font=("Helvetica", 10), - ).pack(anchor="w") - - # 相似度 - tk.Label( - info_frame, text=f"{rank}. 相似度 {sims_val:.2f}", font=("Helvetica", 10, "bold") - ).pack(anchor="w") - - # 右侧阵容区域 - roster_frame = tk.Frame(row) - roster_frame.pack(side="right", fill="both", expand=True) - - # 左右阵容渲染(修改为水平排列) - for side_name, is_left, win, bg_color, fg_color in [ - ("左", True, winL, "#ffe5e5", "#E23F25"), - ("右", False, winR, "#e5e5ff", "#25ace2"), - ]: - pane = tk.Frame( - roster_frame, - bd=2, - relief="solid", - bg=bg_color, - highlightbackground="red" if winL and is_left or winR and not is_left else "#aaa", - highlightthickness=2, - ) - pane.pack(side="left" if is_left else "right", fill="both", expand=True, padx=5) - - # 侧边阵容 - tk.Label( - pane, - text=f"{side_name}边", - fg=fg_color if win else "#666", - bg=bg_color if win else "#f0f0f0", - font=("Helvetica", 9, "bold"), - ).pack(anchor="w", padx=4) - inner = tk.Frame(pane, bg=bg_color if win else "#f0f0f0") - inner.pack(fill="x", padx=4, pady=2) - data = Lh if is_left else Rh - for i, count in enumerate(data): - if count > 0: - img = self.images[str(i + 1)] - tk.Label(inner, image=img, bg=bg_color if win else "#f0f0f0").pack( - side="left", padx=2 - ) - tk.Label( - inner, text=f"×{int(count)}", bg=bg_color if win else "#f0f0f0" - ).pack(side="left", padx=(0, 6)) - self._batch_idx += 1 - # 更新滚动区域 - self.history_canvas.configure(scrollregion=self.history_canvas.bbox("all")) - if end < len(top20): - parent.after(50, lambda: self._render_batch(batch_size)) - - def reset_entries(self): - for entry in self.left_monsters.values(): - entry.delete(0, tk.END) - entry.config(bg="white") # Reset color - for entry in self.right_monsters.values(): - entry.delete(0, tk.END) - entry.config(bg="white") # Reset color - self.result_label.config(text="Prediction: ") - - def get_prediction(self): - # 准备输入数据(完全匹配ArknightsDataset的处理方式) - left_counts = np.zeros(MONSTER_COUNT, dtype=np.int16) - right_counts = np.zeros(MONSTER_COUNT, dtype=np.int16) - # 从界面获取数据(空值处理为0) - for name, entry in self.left_monsters.items(): - value = entry.get() - left_counts[int(name) - 1] = int(value) if value.isdigit() else 0 - for name, entry in self.right_monsters.items(): - value = entry.get() - right_counts[int(name) - 1] = int(value) if value.isdigit() else 0 - - try: - prediction = self.cannot_model.get_prediction(left_counts, right_counts) - return prediction - except FileNotFoundError: - messagebox.showerror("错误", "未找到模型文件,请先点击「训练」按钮") - return 0.5 - except RuntimeError as e: - if "size mismatch" in str(e): - messagebox.showerror("错误", "模型结构不匹配!请删除旧模型并重新训练") - else: - messagebox.showerror("错误", f"模型加载失败: {str(e)}") - return 0.5 - except ValueError: - messagebox.showerror("错误", "请输入有效的数字(0或正整数)") - return 0.5 - except Exception as e: - messagebox.showerror("错误", f"预测时发生错误: {str(e)}") - return 0.5 - - def update_prediction(self, prediction): - # 结果解释(注意:prediction直接对应标签'R'的概率) - right_win_prob = prediction # 模型输出的是右方胜率 - left_win_prob = 1 - right_win_prob - - # 格式化输出 - result_text = ( - f"预测结果:\n\n左方胜率: {left_win_prob:.2%}\t\t" f"右方胜率: {right_win_prob:.2%}" - ) - - # 根据胜率设置颜色(保持与之前一致) - self.result_label.config(text=result_text) - if left_win_prob > 0.7: - self.result_label.config(fg="#E23F25", font=("Helvetica", 12, "bold")) # red - elif left_win_prob > 0.6: - self.result_label.config(fg="#E23F25", font=("Helvetica", 12, "bold")) - elif right_win_prob > 0.7: - self.result_label.config(fg="#25ace2", font=("Helvetica", 12, "bold")) # blue - elif right_win_prob > 0.6: - self.result_label.config(fg="#25ace2", font=("Helvetica", 12, "bold")) - else: - self.result_label.config(fg="black", font=("Helvetica", 12, "bold")) - - def predict(self): - # 保存当前预测结果用于后续数据收集 - self.current_prediction = self.get_prediction() - self.update_prediction(self.current_prediction) - - if self.history_visible: - for w in self.history_frame.winfo_children(): - w.destroy() - self.render_history(self.history_frame) - self.history_canvas.configure(scrollregion=self.history_canvas.bbox("all")) - - def recognize(self): - # 如果正在进行自动获取数据,从adb加载截图 - if hasattr(self, "auto_fetch") and self.auto_fetch.auto_fetch_running: - screenshot = self.adb_connector.capture_screenshot() - else: - screenshot = None - - if self.no_region: # 如果尚未选择区域,从adb获取截图 - if self.first_recognize: # 首次识别时,尝试连接adb - self.adb_connector.connect() - self.first_recognize = False - screenshot = self.adb_connector.capture_screenshot() - - results = self.recognizer.process_regions(screenshot) - self.reset_entries() - return results, screenshot - - def update_monster(self, results): - # 处理结果 - for res in results: - region_id = res["region_id"] - if "error" not in res: - matched_id = res["matched_id"] - number = res["number"] - if matched_id != 0: - if region_id < 3: - entry = self.left_monsters[str(matched_id)] - else: - entry = self.right_monsters[str(matched_id)] - entry.delete(0, tk.END) - entry.insert(0, number) - # Highlight the image if the entry already has data - if entry.get(): - entry.config(bg="yellow") - else: - if "matched_id" in res: - matched_id = res["matched_id"] - if region_id < 3: - entry = self.left_monsters[str(matched_id)] - else: - entry = self.right_monsters[str(matched_id)] - entry.delete(0, tk.END) - entry.config(bg="red") - entry.insert(0, "Error") - - def recognize_and_predict(self): - results, screenshot = self.recognize() - self.update_monster(results) - prediction = self.get_prediction() - self.update_prediction(prediction) - - # 历史对局 - if self.history_visible: - for w in self.history_frame.winfo_children(): - w.destroy() - self.render_history(self.history_frame) - self.history_canvas.configure(scrollregion=self.history_canvas.bbox("all")) - return prediction, results, screenshot - - def reselect_roi(self): - self.recognizer.select_roi() - self.no_region = False - - def start_training(self): - threading.Thread(target=self.train_model).start() - - def train_model(self): - # Update progress - self.root.update_idletasks() - - # Simulate training process - subprocess.run(["python", "train.py"]) - self.root.update_idletasks() - - messagebox.showinfo("Info", "Model trained successfully") - - def start_callback(self): - self.auto_fetch_button.config(text="停止自动获取数据") - pass - - def stop_callback(self): - self.auto_fetch_button.config(text="自动获取数据") - pass - - def update_statistics(self): - elapsed_time = time.time() - self.auto_fetch.start_time if self.auto_fetch.start_time else 0 - hours, remainder = divmod(elapsed_time, 3600) - minutes, _ = divmod(remainder, 60) - stats_text = ( - f"总共填写次数: {self.auto_fetch.total_fill_count} , " - f"填写×次数: {self.auto_fetch.incorrect_fill_count}, " - f"当次运行时长: {int(hours)}小时{int(minutes)}分钟" - ) - self.stats_label.config(text=stats_text) - - def toggle_auto_fetch(self): - if not (hasattr(self, "auto_fetch") and self.auto_fetch.auto_fetch_running): - self.auto_fetch = AutoFetch( - self.adb_connector, - self.game_mode, - self.is_invest, - update_monster_callback=self.update_monster, - update_prediction_callback=self.update_prediction, - updater=self.update_statistics, - start_callback=self.start_callback, - stop_callback=self.stop_callback, - training_duration=float(self.duration_entry.get()) * 3600, # 获取训练时长 - ) - self.auto_fetch.start_auto_fetch() - else: - self.auto_fetch.stop_auto_fetch() - - def update_device_serial(self): - """更新设备序列号""" - new_serial = self.device_serial.get() - self.adb_connector.update_device_serial(new_serial) # 重新初始化设备连接 - messagebox.showinfo("提示", f"已更新模拟器序列号为: {new_serial}") - - -if __name__ == "__main__": - root = tk.Tk() - app = ArknightsApp(root) - root.mainloop() diff --git a/main_sim.py b/main_sim.py index 9ff686b..8395dde 100644 --- a/main_sim.py +++ b/main_sim.py @@ -3,22 +3,21 @@ import tkinter as tk from tkinter import messagebox from PIL import Image, ImageTk -import math -from unit import Unit # 确保 Unit 已导入 -import json # REMOVED_TEAM_INTERFACE: Added missing import for the main block -import random # REMOVED_TEAM_INTERFACE: Added missing import for the main block -import sys # Import sys for stdin +from src.utils.unit import Unit +import json +import sys import logging -from simulator.battle_field import Battlefield # 确保 Battlefield 已导入 -from simulator.monsters import MonsterFactory # 确保 MonsterFactory 已导入 -from simulator.utils import MONSTER_MAPPING, REVERSE_MONSTER_MAPPING, Faction # 根据你的实际路径调整 -from simulator.vector2d import FastVector # 确保 FastVector 已导入 -from simulator.monsters import AttackState, Monster, MonsterFactory -from recognize import MONSTER_COUNT +from src.simulation.battle_field import Battlefield +from src.simulation.utils import Faction +from src.simulation.vector2d import FastVector +from src.simulation.monsters import AttackState, Monster +from src.core.config import MONSTER_DATA, MONSTER_IMAGES +from src.recognition.recognize import MONSTER_COUNT logger = logging.getLogger(__name__) + class AppState(Enum): INITIAL = auto() # 初始状态 SETUP = auto() # 部署阶段 @@ -35,63 +34,81 @@ def __init__(self, ui_update_callback): def transition_to(self, new_state): """状态转换并触发UI更新""" allowed_transitions = { - AppState.INITIAL: [AppState.INITIAL, AppState.SETUP, AppState.ENDED], - AppState.SETUP: [AppState.INITIAL, AppState.SETUP, AppState.SIMULATING], + AppState.INITIAL: [ + AppState.INITIAL, + AppState.SETUP, + AppState.ENDED, + ], + AppState.SETUP: [ + AppState.INITIAL, + AppState.SETUP, + AppState.SIMULATING, + ], AppState.SIMULATING: [AppState.PAUSED, AppState.ENDED], AppState.PAUSED: [AppState.SIMULATING, AppState.SETUP], - AppState.ENDED: [AppState.INITIAL, AppState.SETUP] + AppState.ENDED: [AppState.INITIAL, AppState.SETUP], } if new_state in allowed_transitions[self.state]: self.state = new_state self.ui_update() else: - logger.error(f"Illegal state transition from {self.state} to {new_state}") + logger.error( + f"Illegal state transition from {self.state} to {new_state}" + ) def get_control_states(self): """返回各控件的状态字典""" states = { - 'deploy': {'state': tk.NORMAL, 'text': '部署怪物'}, - 'confirm_start': {'state': tk.DISABLED}, - 'pause': {'state': tk.DISABLED, 'text': '暂停'}, - 'restore': {'state': tk.DISABLED}, - 'speed_entry': {'state': tk.NORMAL}, - 'clear': {'state': tk.NORMAL}, - 'timer': {'text': ''} + "deploy": {"state": tk.NORMAL, "text": "部署怪物"}, + "confirm_start": {"state": tk.DISABLED}, + "pause": {"state": tk.DISABLED, "text": "暂停"}, + "restore": {"state": tk.DISABLED}, + "speed_entry": {"state": tk.NORMAL}, + "clear": {"state": tk.NORMAL}, + "timer": {"text": ""}, } if self.state == AppState.INITIAL: pass elif self.state == AppState.SETUP: - states.update({ - 'deploy': {'state': tk.NORMAL, 'text': '重新部署'}, - 'confirm_start': {'state': tk.NORMAL}, - 'timer': {'text': '未开始'} - }) + states.update( + { + "deploy": {"state": tk.NORMAL, "text": "重新部署"}, + "confirm_start": {"state": tk.NORMAL}, + "timer": {"text": "未开始"}, + } + ) elif self.state == AppState.SIMULATING: - states.update({ - 'deploy': {'state': tk.DISABLED, 'text': '重新部署'}, - 'confirm_start': {'state': tk.DISABLED}, - 'pause': {'state': tk.NORMAL, 'text': '暂停'}, - 'clear': {'state': tk.DISABLED}, - }) + states.update( + { + "deploy": {"state": tk.DISABLED, "text": "重新部署"}, + "confirm_start": {"state": tk.DISABLED}, + "pause": {"state": tk.NORMAL, "text": "暂停"}, + "clear": {"state": tk.DISABLED}, + } + ) elif self.state == AppState.PAUSED: - states.update({ - 'restore': {'state': tk.NORMAL}, - 'pause': {'state': tk.NORMAL, 'text': '继续'}, - 'deploy': {'state': tk.NORMAL, 'text': '重新部署'} - }) + states.update( + { + "restore": {"state": tk.NORMAL}, + "pause": {"state": tk.NORMAL, "text": "继续"}, + "deploy": {"state": tk.NORMAL, "text": "重新部署"}, + } + ) elif self.state == AppState.ENDED: - states.update({ - 'deploy': {'state': tk.DISABLED, 'text': '重新部署'}, - 'restore': {'state': tk.NORMAL}, - 'pause': {'state': tk.DISABLED, 'text': '暂停'}, - 'timer': {'text': '战斗结束'} - }) + states.update( + { + "deploy": {"state": tk.DISABLED, "text": "重新部署"}, + "restore": {"state": tk.NORMAL}, + "pause": {"state": tk.DISABLED, "text": "暂停"}, + "timer": {"text": "战斗结束"}, + } + ) return states @@ -125,7 +142,9 @@ def __init__(self, master: tk.Tk, battle_data): self.initial_battlefield = None # 新增状态 self.setup_phase_active = False # 怪物部署和调整阶段是否激活 - self.selected_monster_for_drag = None # 当前拖动的怪物对象 (Monster 类型) + self.selected_monster_for_drag = ( + None # 当前拖动的怪物对象 (Monster 类型) + ) self.message_label = None # 用于显示提示信息的标签 self.message_timer_id = None # 用于定时清除提示信息的ID @@ -136,25 +155,35 @@ def __init__(self, master: tk.Tk, battle_data): self.state_machine.transition_to(AppState.INITIAL) self.enter_setup_phase() + def _resolve_monster_icon_key(self, monster_name: str) -> str: + """根据怪物名称解析对应的图标文件名。""" + if monster_name in self.monster_icon_key_map: + return self.monster_icon_key_map[monster_name] + + if monster_name in MONSTER_IMAGES: + return monster_name + + return self.default_icon_key + def update_ui_state(self): """根据当前状态更新所有控件状态""" states = self.state_machine.get_control_states() self.deploy_button.config( - state=states['deploy']['state'], - text=states['deploy']['text'] + state=states["deploy"]["state"], text=states["deploy"]["text"] + ) + self.confirm_start_button.config( + state=states["confirm_start"]["state"] ) - self.confirm_start_button.config(state=states['confirm_start']['state']) self.pause_button.config( - state=states['pause']['state'], - text=states['pause']['text'] + state=states["pause"]["state"], text=states["pause"]["text"] ) - self.restore_button.config(state=states['restore']['state']) - self.speed_entry.config(state=states['speed_entry']['state']) - self.clear_button.config(state=states['clear']['state']) + self.restore_button.config(state=states["restore"]["state"]) + self.speed_entry.config(state=states["speed_entry"]["state"]) + self.clear_button.config(state=states["clear"]["state"]) - if states['timer']['text'] != '': - self.timer_label.config(text=states['timer']['text']) + if states["timer"]["text"] != "": + self.timer_label.config(text=states["timer"]["text"]) def hide_window(self): if self.state_machine.state == AppState.SIMULATING: @@ -164,8 +193,10 @@ def hide_window(self): def load_assets(self): self.icons = {} + self.monster_icon_key_map = {} + self.default_icon_key = "empty" if "empty" in MONSTER_IMAGES else "" try: - with open("simulator/monsters.json", encoding='utf-8') as f: + with open("src/simulation/monsters.json", encoding="utf-8") as f: self.monster_data = json.load(f)["monsters"] except FileNotFoundError: logger.error("错误: monsters.json 未找到,请检查路径!") @@ -173,21 +204,47 @@ def load_assets(self): return - for i in range(self.num_monsters): - image_file_id = i + 1 + # 构建“名称/原始名称 -> 图标文件名”的映射,优先使用原始名称对应的图片。 + for _, row in MONSTER_DATA.iterrows(): + monster_name = str(row["名称"]) + original_name = str(row["原始名称"]) + + icon_key = None + if original_name in MONSTER_IMAGES: + icon_key = original_name + elif monster_name in MONSTER_IMAGES: + icon_key = monster_name + + if icon_key is None: + icon_key = self.default_icon_key + + self.monster_icon_key_map[monster_name] = icon_key + self.monster_icon_key_map[original_name] = icon_key + + # 为实际存在的图片创建 Tk 图像,未命中的统一回退到灰色或 empty。 + for image_key, image_array in MONSTER_IMAGES.items(): try: - image = Image.open(f'images/{image_file_id}.png') - self.icons[i] = { - "red": ImageTk.PhotoImage(image.resize((40, 40))), - "blue": ImageTk.PhotoImage(image.resize((40, 40)).transpose(Image.FLIP_LEFT_RIGHT)) + if image_array is None: + raise ValueError("图像数据为空") + + image = Image.fromarray(image_array[:, :, ::-1]) + image_40 = image.resize((40, 40)) + self.icons[image_key] = { + "red": ImageTk.PhotoImage(image_40, master=self.master), + "blue": ImageTk.PhotoImage( + image_40.transpose(Image.FLIP_LEFT_RIGHT), + master=self.master, + ), } except Exception as e: - # 同样,show_message_below_button 可能还不可用 - logger.error(f"加载图标错误 (图标键: {i}, 文件名ID: {image_file_id}): {str(e)}") - self.icons[i] = { - "red": ImageTk.PhotoImage(Image.new("RGB", (40, 40), "gray")), - "blue": ImageTk.PhotoImage(Image.new("RGB", (40, 40), "gray")) - } + logger.error(f"加载图标错误 (图标键: {image_key}): {str(e)}") + + if self.default_icon_key and self.default_icon_key not in self.icons: + fallback = Image.new("RGB", (40, 40), "gray") + self.icons[self.default_icon_key] = { + "red": ImageTk.PhotoImage(fallback, master=self.master), + "blue": ImageTk.PhotoImage(fallback, master=self.master), + } def init_battlefield_for_setup(self): self.state_machine.transition_to(AppState.SETUP) @@ -197,7 +254,9 @@ def init_battlefield_for_setup(self): left_army_config = self.battle_data.get("left", {}) right_army_config = self.battle_data.get("right", {}) - self.battle_field.setup_battle(left_army_config, right_army_config, self.monster_data) + self.battle_field.setup_battle( + left_army_config, right_army_config, self.monster_data + ) while self.battle_field.gameTime < 6.0: result = self.battle_field.run_one_frame() if result: @@ -205,7 +264,10 @@ def init_battlefield_for_setup(self): self.refresh_canvas_display() def on_mouse_drag(self, event): - if self.selected_monster_for_drag and self.state_machine.state in [AppState.PAUSED, AppState.SETUP]: + if self.selected_monster_for_drag and self.state_machine.state in [ + AppState.PAUSED, + AppState.SETUP, + ]: new_grid_x = event.x / self.cell_size new_grid_y = event.y / self.cell_size new_grid_x = max(0.25, min(new_grid_x, self.grid_width - 0.25)) @@ -213,7 +275,9 @@ def on_mouse_drag(self, event): self.selected_monster_for_drag.position.x = new_grid_x self.selected_monster_for_drag.position.y = new_grid_y - self.selected_monster_for_drag.target_deployment_position = FastVector(new_grid_x, new_grid_y) + self.selected_monster_for_drag.target_deployment_position = ( + FastVector(new_grid_x, new_grid_y) + ) self.refresh_canvas_display() @@ -230,25 +294,44 @@ def create_widgets(self): self.speed_entry = tk.Entry(speed_frame, width=5) self.speed_entry.pack(side=tk.LEFT) self.speed_entry.insert(0, f"{int(self.speed_multiplier)}") - tk.Button(speed_frame, text="应用", command=self.apply_speed).pack(side=tk.LEFT) + tk.Button(speed_frame, text="应用", command=self.apply_speed).pack( + side=tk.LEFT + ) - self.deploy_button = tk.Button(top_control_frame, text="部署怪物", command=self.enter_setup_phase) + self.deploy_button = tk.Button( + top_control_frame, text="部署怪物", command=self.enter_setup_phase + ) self.deploy_button.pack(side=tk.LEFT, padx=5) - self.confirm_start_button = tk.Button(top_control_frame, text="开始战斗", command=self.start_actual_simulation, - state=tk.DISABLED) + self.confirm_start_button = tk.Button( + top_control_frame, + text="开始战斗", + command=self.start_actual_simulation, + state=tk.DISABLED, + ) self.confirm_start_button.pack(side=tk.LEFT, padx=5) - self.clear_button = tk.Button(top_control_frame, text="清空战场", command=self.clear_sandbox) + self.clear_button = tk.Button( + top_control_frame, text="清空战场", command=self.clear_sandbox + ) self.clear_button.pack(side=tk.LEFT, padx=5) self.timer_label = tk.Label(top_control_frame, text="0.00秒") self.timer_label.pack(side=tk.LEFT, padx=100) - self.pause_button = tk.Button(top_control_frame, text="暂停", command=self.toggle_pause, state=tk.DISABLED) + self.pause_button = tk.Button( + top_control_frame, + text="暂停", + command=self.toggle_pause, + state=tk.DISABLED, + ) self.pause_button.pack(side=tk.LEFT, padx=5) - self.restore_button = tk.Button(top_control_frame, text="恢复站位", command=self.restore_initial_positions, - state=tk.DISABLED) + self.restore_button = tk.Button( + top_control_frame, + text="恢复站位", + command=self.restore_initial_positions, + state=tk.DISABLED, + ) self.restore_button.pack(side=tk.LEFT, padx=5) self.game_over_label = tk.Label(top_control_frame, text="", fg="red") @@ -258,7 +341,12 @@ def create_widgets(self): self.message_label = tk.Label(control_frame, text="", fg="black") self.message_label.pack(pady=2) - self.canvas = tk.Canvas(self.master, width=self.canvas_width, height=self.canvas_height, bg='white') + self.canvas = tk.Canvas( + self.master, + width=self.canvas_width, + height=self.canvas_height, + bg="white", + ) self.canvas.pack(pady=10) self.draw_grid() @@ -266,13 +354,19 @@ def create_widgets(self): self.canvas.bind("", self.on_mouse_drag) self.canvas.bind("", self.on_mouse_up) - def show_message_below_button(self, message, is_error=False, duration=5000): + def show_message_below_button( + self, message, is_error=False, duration=5000 + ): """在按钮下方显示文字提示,并在指定时间后消失""" if self.message_label: - self.message_label.config(text=message, fg="red" if is_error else "black") + self.message_label.config( + text=message, fg="red" if is_error else "black" + ) if self.message_timer_id: self.master.after_cancel(self.message_timer_id) - self.message_timer_id = self.master.after(duration, self.clear_message_below_button) + self.message_timer_id = self.master.after( + duration, self.clear_message_below_button + ) def clear_message_below_button(self): """清除提示信息""" @@ -301,37 +395,82 @@ def apply_speed(self): def draw_grid(self): danger_zone = 0 if self.battle_field and self.battle_field.danger_zone_size() > 0: - danger_zone = min(self.battle_field.danger_zone_size(), self.grid_height / 2 + 1) - self.canvas.create_rectangle(0, 0, self.canvas_width, danger_zone * self.cell_size, fill='#cccc00', - outline="") - self.canvas.create_rectangle(0, 0, (danger_zone+1) * self.cell_size , self.canvas_height, fill='#cccc00', - outline="") - self.canvas.create_rectangle(self.canvas_width, 0, self.canvas_width - (danger_zone+1) * self.cell_size, - self.canvas_height, fill='#cccc00', outline="") - self.canvas.create_rectangle(self.canvas_width, self.canvas_height, 0, - self.canvas_height - danger_zone * self.cell_size, fill='#cccc00', outline="") + danger_zone = min( + self.battle_field.danger_zone_size(), self.grid_height / 2 + 1 + ) + self.canvas.create_rectangle( + 0, + 0, + self.canvas_width, + danger_zone * self.cell_size, + fill="#cccc00", + outline="", + ) + self.canvas.create_rectangle( + 0, + 0, + (danger_zone + 1) * self.cell_size, + self.canvas_height, + fill="#cccc00", + outline="", + ) + self.canvas.create_rectangle( + self.canvas_width, + 0, + self.canvas_width - (danger_zone + 1) * self.cell_size, + self.canvas_height, + fill="#cccc00", + outline="", + ) + self.canvas.create_rectangle( + self.canvas_width, + self.canvas_height, + 0, + self.canvas_height - danger_zone * self.cell_size, + fill="#cccc00", + outline="", + ) # 绘制最左侧的柔和红色列 - soft_red_fill = '#F08080' # 亮珊瑚色 - soft_red_line = '#D87070' # 稍暗的亮珊瑚色 - self.canvas.create_rectangle(0, 0, self.cell_size, self.canvas_height, fill=soft_red_fill, outline="") + soft_red_fill = "#F08080" # 亮珊瑚色 + soft_red_line = "#D87070" # 稍暗的亮珊瑚色 + self.canvas.create_rectangle( + 0, + 0, + self.cell_size, + self.canvas_height, + fill=soft_red_fill, + outline="", + ) # 绘制最右侧的柔和蓝色列 - soft_blue_fill = '#ADD8E6' # 浅蓝色 - soft_blue_line = '#9CC2D0' # 稍暗的浅蓝色 - self.canvas.create_rectangle(self.canvas_width - self.cell_size, 0, self.canvas_width, self.canvas_height, - fill=soft_blue_fill, outline="") + soft_blue_fill = "#ADD8E6" # 浅蓝色 + soft_blue_line = "#9CC2D0" # 稍暗的浅蓝色 + self.canvas.create_rectangle( + self.canvas_width - self.cell_size, + 0, + self.canvas_width, + self.canvas_height, + fill=soft_blue_fill, + outline="", + ) for i in range(self.grid_width + 1): x = i * self.cell_size if i == 0 or i == self.grid_width: continue if i == 1: # 红色列的右边缘线 - self.canvas.create_line(x, 0, x, self.canvas_height, fill=soft_red_line) + self.canvas.create_line( + x, 0, x, self.canvas_height, fill=soft_red_line + ) elif i == self.grid_width - 1: # 蓝色列的左边缘线 - self.canvas.create_line(x, 0, x, self.canvas_height, fill=soft_blue_line) + self.canvas.create_line( + x, 0, x, self.canvas_height, fill=soft_blue_line + ) else: - self.canvas.create_line(x, 0, x, self.canvas_height, fill='lightgray') # 垂直网格线 + self.canvas.create_line( + x, 0, x, self.canvas_height, fill="lightgray" + ) # 垂直网格线 # 绘制水平网格线 for i in range(self.grid_height + 1): @@ -339,27 +478,43 @@ def draw_grid(self): # 左边红色区域的水平线可以特殊处理,使其在红色背景上更明显,或者保持lightgray if self.cell_size > 0: # 确保 cell_size > 0 避免问题 # 在红色区域内绘制颜色稍浅的水平线,或者使用对比色 - self.canvas.create_line(0, y, self.cell_size, y, fill='#E07070') # 示例:红色区域内的水平线 + self.canvas.create_line( + 0, y, self.cell_size, y, fill="#E07070" + ) # 示例:红色区域内的水平线 # 在蓝色区域内绘制颜色稍浅的水平线 - self.canvas.create_line(self.canvas_width - self.cell_size, y, self.canvas_width, y, - fill='#9CBED0') # 示例:蓝色区域内的水平线 + self.canvas.create_line( + self.canvas_width - self.cell_size, + y, + self.canvas_width, + y, + fill="#9CBED0", + ) # 示例:蓝色区域内的水平线 # 中间区域的水平线 - self.canvas.create_line(self.cell_size, y, self.canvas_width - self.cell_size, y, fill='lightgray') + self.canvas.create_line( + self.cell_size, + y, + self.canvas_width - self.cell_size, + y, + fill="lightgray", + ) else: # 如果 cell_size 为0或负,则绘制完整线条 - self.canvas.create_line(0, y, self.canvas_width, y, fill='lightgray') + self.canvas.create_line( + 0, y, self.canvas_width, y, fill="lightgray" + ) def refresh_canvas_display(self): - if not self.canvas: return + if not self.canvas: + return self.canvas.delete("all") self.draw_grid() self.canvas.delete("victory_text") # 清除可能存在的胜利信息 if len(self.battle_field.monsters) > len(self.units): for i in range(len(self.units), len(self.battle_field.monsters)): - new_unit = Unit('red', 0, 0, 0) + new_unit = Unit("red", 0, 0, 0) self.units.append(new_unit) elif len(self.battle_field.monsters) < len(self.units): - self.units = self.units[:len(self.battle_field.monsters)] + self.units = self.units[: len(self.battle_field.monsters)] index = 0 for monster in self.battle_field.monsters: @@ -368,14 +523,11 @@ def refresh_canvas_display(self): if monster and monster.is_alive: ui_unit.x = monster.position.x ui_unit.y = monster.position.y - ui_unit.team = 'red' if monster.faction == Faction.LEFT else 'blue' - display_id_for_icon = REVERSE_MONSTER_MAPPING.get(monster.name) - if display_id_for_icon is None: - logger.error(f"怪物名 {monster.name} 在 REVERSE_MONSTER_MAPPING 中未找到!") - self.show_message_below_button(f"怪物名 {monster.name} 在 REVERSE_MONSTER_MAPPING 中未找到!", - is_error=True) - display_id_for_icon = 0 - ui_unit.unit_id = display_id_for_icon + ui_unit.team = ( + "red" if monster.faction == Faction.LEFT else "blue" + ) + ui_unit.unit_id = self._resolve_monster_icon_key(monster.name) + ui_unit.monster_name = monster.name ui_unit.health = monster.health ui_unit.max_health = monster.max_health ui_unit.skill = monster.get_skill_bar() @@ -387,42 +539,84 @@ def refresh_canvas_display(self): for monster_obj in self.battle_field.monsters: if monster_obj.is_alive and monster_obj.target is not None: self.canvas.create_line( - monster_obj.position.x * self.cell_size, monster_obj.position.y * self.cell_size, - monster_obj.target.position.x * self.cell_size, monster_obj.target.position.y * self.cell_size, - fill="#FF3030" if monster_obj.faction == Faction.LEFT else "#3030FF", width=1, arrow='last') + monster_obj.position.x * self.cell_size, + monster_obj.position.y * self.cell_size, + monster_obj.target.position.x * self.cell_size, + monster_obj.target.position.y * self.cell_size, + fill=( + "#FF3030" + if monster_obj.faction == Faction.LEFT + else "#3030FF" + ), + width=1, + arrow="last", + ) self.timer_label.config(text=f"{self.battle_field.gameTime:.2f}秒") - def draw_unit(self, unit, monster: 'Monster'): + def draw_unit(self, unit, monster: "Monster"): x_pixel = unit.x * self.cell_size y_pixel = unit.y * self.cell_size icon_to_draw = None - if unit.unit_id in self.icons and unit.team in self.icons[unit.unit_id]: - icon_to_draw = self.icons[unit.unit_id][unit.team] + icon_key = getattr(unit, "unit_id", self.default_icon_key) + if icon_key not in self.icons: + icon_key = self.default_icon_key + + if icon_key in self.icons and unit.team in self.icons[icon_key]: + icon_to_draw = self.icons[icon_key][unit.team] else: - self.canvas.create_rectangle(x_pixel - 20, y_pixel - 20, x_pixel + 20, y_pixel + 20, fill="gray", - tags=("unit",)) + self.canvas.create_rectangle( + x_pixel - 20, + y_pixel - 20, + x_pixel + 20, + y_pixel + 20, + fill="gray", + tags=("unit",), + ) return - self.canvas.create_image(x_pixel, y_pixel, image=icon_to_draw, tags=("unit",)) + self.canvas.create_image( + x_pixel, y_pixel, image=icon_to_draw, tags=("unit",) + ) bar_width = 40 bar_height = 5 health_bar_y = y_pixel + 25 - health_ratio = min(1, unit.health / unit.max_health) if unit.max_health > 0 else 0 + health_ratio = ( + min(1, unit.health / unit.max_health) if unit.max_health > 0 else 0 + ) current_health_width = max(0, bar_width * health_ratio) - self.canvas.create_rectangle(x_pixel - bar_width / 2, health_bar_y - bar_height / 2, x_pixel + bar_width / 2, - health_bar_y + bar_height / 2, fill="#400000", outline="") + self.canvas.create_rectangle( + x_pixel - bar_width / 2, + health_bar_y - bar_height / 2, + x_pixel + bar_width / 2, + health_bar_y + bar_height / 2, + fill="#400000", + outline="", + ) if current_health_width > 0: - self.canvas.create_rectangle(x_pixel - bar_width / 2, health_bar_y - bar_height / 2, - x_pixel - bar_width / 2 + current_health_width, health_bar_y + bar_height / 2, - fill="#FF3030", outline="") + self.canvas.create_rectangle( + x_pixel - bar_width / 2, + health_bar_y - bar_height / 2, + x_pixel - bar_width / 2 + current_health_width, + health_bar_y + bar_height / 2, + fill="#FF3030", + outline="", + ) skill_bar_y = health_bar_y + 7 - skill_ratio = min(1, unit.skill / unit.max_skill) if unit.max_skill > 0 else 0 + skill_ratio = ( + min(1, unit.skill / unit.max_skill) if unit.max_skill > 0 else 0 + ) current_skill_width = bar_width * skill_ratio - self.canvas.create_rectangle(x_pixel - bar_width / 2, skill_bar_y - bar_height / 2, x_pixel + bar_width / 2, - skill_bar_y + bar_height / 2, fill="black", outline="") + self.canvas.create_rectangle( + x_pixel - bar_width / 2, + skill_bar_y - bar_height / 2, + x_pixel + bar_width / 2, + skill_bar_y + bar_height / 2, + fill="black", + outline="", + ) attack_state_color = "#ffcccc" if monster.attack_state == AttackState.后摇: @@ -431,9 +625,14 @@ def draw_unit(self, unit, monster: 'Monster'): attack_state_color = "yellow" if current_skill_width > 0: - self.canvas.create_rectangle(x_pixel - bar_width / 2, skill_bar_y - bar_height / 2, - x_pixel - bar_width / 2 + current_skill_width, skill_bar_y + bar_height / 2, - fill=attack_state_color, outline="") + self.canvas.create_rectangle( + x_pixel - bar_width / 2, + skill_bar_y - bar_height / 2, + x_pixel - bar_width / 2 + current_skill_width, + skill_bar_y + bar_height / 2, + fill=attack_state_color, + outline="", + ) def on_mouse_down(self, event): grid_x = event.x / self.cell_size @@ -442,13 +641,18 @@ def on_mouse_down(self, event): if self.state_machine.state in [AppState.PAUSED, AppState.SETUP]: clicked_monster_obj = None for unit_in_list in reversed(self.units): - monster = self.battle_field.get_monster_with_id(unit_in_list.monster_global_id) - if not monster: continue + monster = self.battle_field.get_monster_with_id( + unit_in_list.monster_global_id + ) + if not monster: + continue monster_center_x_px = monster.position.x * self.cell_size monster_center_y_px = monster.position.y * self.cell_size icon_half_size_px = 20 - if (abs(event.x - monster_center_x_px) < icon_half_size_px and - abs(event.y - monster_center_y_px) < icon_half_size_px): + if ( + abs(event.x - monster_center_x_px) < icon_half_size_px + and abs(event.y - monster_center_y_px) < icon_half_size_px + ): clicked_monster_obj = monster break if clicked_monster_obj: @@ -456,14 +660,19 @@ def on_mouse_down(self, event): return def on_mouse_drag(self, event): - if self.selected_monster_for_drag and self.state_machine.state in [AppState.PAUSED, AppState.SETUP]: + if self.selected_monster_for_drag and self.state_machine.state in [ + AppState.PAUSED, + AppState.SETUP, + ]: new_grid_x = event.x / self.cell_size new_grid_y = event.y / self.cell_size new_grid_x = max(0.25, min(new_grid_x, self.grid_width - 0.25)) new_grid_y = max(0.25, min(new_grid_y, self.grid_height - 0.25)) self.selected_monster_for_drag.position.x = new_grid_x self.selected_monster_for_drag.position.y = new_grid_y - self.selected_monster_for_drag.target = self.selected_monster_for_drag.find_target() + self.selected_monster_for_drag.target = ( + self.selected_monster_for_drag.find_target() + ) self.refresh_canvas_display() def on_mouse_up(self, event): @@ -525,14 +734,20 @@ def simulate(self): # 在画布中央放大显示信息 self.canvas.create_text( - self.canvas_width / 2, self.canvas_height / 2, - text=game_over_message, font=font_style, fill=text_color, tags="victory_text" + self.canvas_width / 2, + self.canvas_height / 2, + text=game_over_message, + font=font_style, + fill=text_color, + tags="victory_text", ) else: interval = max(1, 33) self.simulation_id = self.master.after(interval, self.simulate) - def show_result(self, message): # 此方法现在主要被画布显示和按钮下方提示替代 + def show_result( + self, message + ): # 此方法现在主要被画布显示和按钮下方提示替代 self.show_message_below_button(message) def clear_sandbox(self): @@ -556,7 +771,9 @@ def restore_initial_positions(self): self.show_message_below_button("没有可恢复的初始站位") return self.battle_field = copy.deepcopy(self.initial_battlefield) - self.battle_field.gameTime = self.initial_battlefield.gameTime # 保持时间连续性 + self.battle_field.gameTime = ( + self.initial_battlefield.gameTime + ) # 保持时间连续性 if self.state_machine.state in [AppState.PAUSED, AppState.ENDED]: self.state_machine.transition_to(AppState.SETUP) self.refresh_canvas_display() # 会清除 "victory_text" @@ -567,10 +784,13 @@ def main(): root = tk.Tk() # root.withdraw() # 如果不需要立即隐藏主窗口,可以注释掉 - initial_battle_setup = {"left": {"Vvan": 4, "炮击组长": 3, "“庞贝”": 2}, "right": {"大喷蛛": 6, "冰爆源石虫": 23}, - "result": "left"} + initial_battle_setup = { + "left": {"Vvan": 4, "炮击组长": 3, "“庞贝”": 2}, + "right": {"大喷蛛": 6, "冰爆源石虫": 23}, + "result": "left", + } - sys.stdin.reconfigure(encoding='utf-8') + sys.stdin.reconfigure(encoding="utf-8") try: if not sys.stdin.isatty(): json_data = sys.stdin.read() @@ -582,10 +802,14 @@ def main(): else: logger.info("stdin 是交互式终端,使用默认配置。") except json.JSONDecodeError: - logger.error("错误: 无法解析 stdin 中的 JSON 数据,请检查格式,将使用默认配置。") + logger.error( + "错误: 无法解析 stdin 中的 JSON 数据,请检查格式,将使用默认配置。" + ) except Exception as e: - logger.error(f"从 stdin 读取或解析时发生未知错误: {e},将使用默认配置。") + logger.error( + f"从 stdin 读取或解析时发生未知错误: {e},将使用默认配置。" + ) try: app = SandboxSimulator(root, initial_battle_setup) @@ -599,26 +823,41 @@ def main(): if not app.monster_data: # 检查 monsters.json 是否加载成功 error_messages.append("错误: monsters.json 未找到或为空,请检查文件!") - if len(initial_battle_setup.get("left", {})) == 0 or len(initial_battle_setup.get("right", {})) == 0: + if ( + len(initial_battle_setup.get("left", {})) == 0 + or len(initial_battle_setup.get("right", {})) == 0 + ): error_messages.append("错误:至少一方的怪物列表为空!") problematic_monsters_found = [] - problematic_monster_names = ["矿脉守卫", "提亚卡乌好战者", "凋零萨卡兹", "狂暴宿主组长", "高能源石虫"] + problematic_monster_names = [ + "矿脉守卫", + "提亚卡乌好战者", + "凋零萨卡兹", + "狂暴宿主组长", + "高能源石虫", + ] for team_key in ["left", "right"]: for monster_name in initial_battle_setup.get(team_key, {}): if monster_name in problematic_monster_names: problematic_monsters_found.append( - f"{('左方' if team_key == 'left' else '右方')}存在问题怪物: {monster_name}") + f"{('左方' if team_key == 'left' else '右方')}存在问题怪物: {monster_name}" + ) if problematic_monsters_found: - error_messages.append("警告: " + "; ".join(problematic_monsters_found)) + error_messages.append( + "警告: " + "; ".join(problematic_monsters_found) + ) if error_messages: - app.show_message_below_button(" | ".join(error_messages), is_error=True, duration=10000) + app.show_message_below_button( + " | ".join(error_messages), is_error=True, duration=10000 + ) if "monsters.json 未找到或为空" in " | ".join(error_messages): - logger.error("由于 monsters.json 缺失或错误,模拟器可能无法正常工作。") - + logger.error( + "由于 monsters.json 缺失或错误,模拟器可能无法正常工作。" + ) if not root.winfo_exists(): return @@ -630,8 +869,10 @@ def main(): logging.getLogger().setLevel(logging.DEBUG) logging.getLogger("PIL").setLevel(logging.INFO) stream_handler = logging.StreamHandler() - stream_handler.setFormatter(logging.Formatter( - "%(asctime)s - %(name)s - %(levelname)s - %(message)s", - )) + stream_handler.setFormatter( + logging.Formatter( + "%(asctime)s - %(name)s - %(levelname)s - %(message)s", + ) + ) logging.getLogger().addHandler(stream_handler) main() diff --git a/models/update.md b/models/update.md deleted file mode 100644 index fd552e3..0000000 --- a/models/update.md +++ /dev/null @@ -1,7 +0,0 @@ -# 这个文件用来放模型更新日志(如果有人想写的话) ----- -2025/5/6: - -- 数据库:dataset56f_64k -- 超参数:256/4/8/5e-4/30/1145 -- 准确率:93.23%/89.32% \ No newline at end of file diff --git a/multi_instance.py b/multi_instance.py index b9307c2..4f74b93 100644 --- a/multi_instance.py +++ b/multi_instance.py @@ -5,47 +5,61 @@ import subprocess from pathlib import Path from PyQt6.QtWidgets import ( - QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, - QPushButton, QLabel, QPlainTextEdit, QSpinBox, QComboBox, QCheckBox, - QMessageBox, QSplitter, QScrollArea, QFrame, QLineEdit + QApplication, + QMainWindow, + QWidget, + QVBoxLayout, + QHBoxLayout, + QPushButton, + QLabel, + QPlainTextEdit, + QComboBox, + QCheckBox, + QMessageBox, + QSplitter, + QScrollArea, + QFrame, + QLineEdit, ) -from PyQt6.QtCore import Qt, QTimer, pyqtSignal, QObject, pyqtSlot +from PyQt6.QtCore import Qt, QTimer, pyqtSignal from PyQt6.QtGui import QFont -import loadData -import auto_fetch -import data_package -from recognize import MONSTER_COUNT -from login import LoginManager -from config import FIELD_FEATURE_COUNT +from src.data import load_data +from src.game import auto_fetch +from src.data import data_package +from src.game.login import LoginManager +from src.core.config import FIELD_FEATURE_COUNT class LogDisplay(QPlainTextEdit): log_signal = pyqtSignal(str) - + def __init__(self, parent=None): super().__init__(parent) self.setReadOnly(True) self._auto_scroll = True self.log_signal.connect(self._on_log) - + def is_at_bottom(self): scrollbar = self.verticalScrollBar() return scrollbar.value() >= scrollbar.maximum() - 10 - + def scrollContentsBy(self, dx, dy): super().scrollContentsBy(dx, dy) self._auto_scroll = self.is_at_bottom() - + def _on_log(self, text): was_at_bottom = self._auto_scroll self.appendPlainText(text) if was_at_bottom: - self.verticalScrollBar().setValue(self.verticalScrollBar().maximum()) - + self.verticalScrollBar().setValue( + self.verticalScrollBar().maximum() + ) + def append_log(self, text): self.log_signal.emit(text) + logging.getLogger().setLevel(logging.DEBUG) logging.getLogger("PIL").setLevel(logging.INFO) stream_handler = logging.StreamHandler() @@ -60,6 +74,7 @@ def append_log(self, text): class SmartPortsLineEdit(QLineEdit): """智能端口输入框:支持延迟格式化、失去焦点格式化和粘贴立即格式化""" + def __init__(self, parent=None): super().__init__(parent) self.format_timer = QTimer(self) @@ -84,19 +99,26 @@ def _format_text(self): if self._formatting: return self._formatting = True - + text = self.text() - parts = text.replace('\n', ',').replace(',', ',').replace(';', ',').replace(' ', ',').split(',') + parts = ( + text.replace("\n", ",") + .replace(",", ",") + .replace(";", ",") + .replace(" ", ",") + .split(",") + ) ports = [p.strip() for p in parts if p.strip().isdigit()] formatted = ", ".join(ports) - + if text != formatted and ports: cursor_pos = self.cursorPosition() self.setText(formatted) self.setCursorPosition(min(cursor_pos, len(formatted))) - + self._formatting = False + # 用于共享资源密集型对象,减少多开时的内存占用 _cannot_model = None _recognizer = None @@ -109,12 +131,14 @@ def get_cannot_model(): if _cannot_model is None: logger.info("首次初始化 CannotModel...") try: - from predict import CannotModel + from src.models.predict import CannotModel + logger.info("Using PyTorch model for predictions.") except Exception: - from predict_onnx import CannotModel + from src.models.predict_onnx import CannotModel + logger.info("Using ONNX model for predictions.") - + _cannot_model = CannotModel() logger.info("CannotModel 初始化完成") return _cannot_model @@ -125,7 +149,8 @@ def get_recognizer(): global _recognizer if _recognizer is None: logger.info("首次初始化 RecognizeMonster...") - from recognize import RecognizeMonster + from src.recognition.recognize import RecognizeMonster + _recognizer = RecognizeMonster(method="ADB") logger.info("RecognizeMonster 初始化完成") return _recognizer @@ -136,7 +161,8 @@ def get_field_recognizer(): global _field_recognizer if _field_recognizer is None: logger.info("首次初始化 FieldRecognizer...") - from field_recognition import FieldRecognizer + from src.recognition.field_recognition import FieldRecognizer + _field_recognizer = FieldRecognizer() logger.info("FieldRecognizer 初始化完成") return _field_recognizer @@ -155,12 +181,14 @@ class DeviceInstance: def __init__(self, port): self.port = port self.serial = f"127.0.0.1:{port}" - self.connector = loadData.AdbConnector(self.serial) + self.connector = load_data.AdbConnector(self.serial) self.auto_fetch = None self.login_manager = None self.status = "已停止" self.auto_fetch_thread = None # 保存线程引用 - self.stop_event = threading.Event() # 使用Event对象进行线程间通信,更可靠 + self.stop_event = ( + threading.Event() + ) # 使用Event对象进行线程间通信,更可靠 self.start_time = None # 实例启动时间戳 self.last_activity_time = time.time() # 最后活动时间戳(用于检测崩溃) self.thread_running = False # 线程是否正在运行的标志 @@ -172,49 +200,56 @@ def start(self, game_mode, is_invest): # 保存设置 self.game_mode = game_mode self.is_invest = is_invest - + # 重置停止事件和标志 self.stop_event.clear() self.thread_running = True self.last_activity_time = time.time() self.status = "连接中" - - logger.info(f"[{self.serial}] 开始启动实例,游戏模式: {game_mode}, 自动投资: {is_invest}") - + + logger.info( + f"[{self.serial}] 开始启动实例,游戏模式: {game_mode}, 自动投资: {is_invest}" + ) + # 记录实例启动时间戳 self.start_time = time.time() - + self.connector.connect() if not self.connector.is_connected: self.status = "连接失败" logger.error(f"[{self.serial}] 连接失败") self.thread_running = False return False - + self.login_manager = LoginManager(self.connector) logger.info(f"[{self.serial}] 尝试首次启动自动登录") self.status = "登录中" - login_success = self.login_manager.auto_login_with_restart(first_start=True, stop_callback=lambda: not self.stop_event.is_set()) - + login_success = self.login_manager.auto_login_with_restart( + first_start=True, + stop_callback=lambda: not self.stop_event.is_set(), + ) + # 检查是否在登录过程中用户点击了停止 if self.stop_event.is_set(): logger.info(f"[{self.serial}] 登录过程被用户停止") self.status = "已停止" self.thread_running = False return False - + if login_success: logger.info(f"[{self.serial}] 首次启动自动登录成功") else: - logger.warning(f"[{self.serial}] 首次启动自动登录失败,继续启动") - + logger.warning( + f"[{self.serial}] 首次启动自动登录失败,继续启动" + ) + # 再次检查停止标志(防止在登录成功后、创建auto_fetch前用户点击停止) if self.stop_event.is_set(): logger.info(f"[{self.serial}] 用户在登录成功后点击了停止") self.status = "已停止" self.thread_running = False return False - + self.auto_fetch = auto_fetch.AutoFetch( self.connector, game_mode, @@ -227,7 +262,9 @@ def start(self, game_mode, is_invest): training_duration=-1, recognizer=get_recognizer(), cannot_model=get_cannot_model(), - field_recognizer=get_field_recognizer() if FIELD_FEATURE_COUNT > 0 else None, + field_recognizer=( + get_field_recognizer() if FIELD_FEATURE_COUNT > 0 else None + ), start_timestamp=self.start_time, # 传递实例启动时间戳 ) logger.info(f"[{self.serial}] 初始化 AutoFetch 成功") @@ -240,11 +277,11 @@ def start(self, game_mode, is_invest): logger.error(f"[{self.serial}] 启动失败: {str(e)}") self.thread_running = False return False - + def _update_activity_time(self, *args, **kwargs): """更新活动时间""" self.last_activity_time = time.time() - + def _on_stop_callback(self): """当 auto_fetch 停止时的回调""" self.thread_running = False @@ -255,7 +292,7 @@ def stop(self): # 设置停止事件,用于中断登录流程 self.stop_event.set() self.thread_running = False - + if self.auto_fetch: # 强制设置停止标志,不等待线程退出 self.auto_fetch.auto_fetch_running = False @@ -263,7 +300,7 @@ def stop(self): else: # 如果 auto_fetch 还未创建,说明可能正在登录过程中 logger.info(f"[{self.serial}] auto_fetch 尚未创建,停止登录过程") - + # 状态改为已停止(stop_event 会在下次启动时自动清除) self.status = "已停止" logger.info(f"[{self.serial}] 强制停止成功,状态: {self.status}") @@ -271,67 +308,74 @@ def stop(self): def get_status_line(self): if not self.auto_fetch or not self.auto_fetch.auto_fetch_running: return f"[{self.serial:<15}] 状态: {self.status}" - + af = self.auto_fetch elapsed = time.time() - af.start_time if af.start_time else 0 hours, remainder = divmod(elapsed, 3600) minutes, _ = divmod(remainder, 60) - + state_name = "过场动画" - if hasattr(af, 'last_state') and af.last_state: - state_name = af.last_state.name if hasattr(af.last_state, 'name') else str(af.last_state) - - return (f"[{self.serial:<15}] " - f"状态: {state_name:<8} | " - f"填写: {af.total_fill_count:<3} | " - f"错误: {af.incorrect_fill_count:<3} | " - f"预测: {af.current_prediction:.2f} | " - f"时长: {int(hours)}h {int(minutes)}m") + if hasattr(af, "last_state") and af.last_state: + state_name = ( + af.last_state.name + if hasattr(af.last_state, "name") + else str(af.last_state) + ) + + return ( + f"[{self.serial:<15}] " + f"状态: {state_name:<8} | " + f"填写: {af.total_fill_count:<3} | " + f"错误: {af.incorrect_fill_count:<3} | " + f"预测: {af.current_prediction:.2f} | " + f"时长: {int(hours)}h {int(minutes)}m" + ) + class MultiInstanceManager(QMainWindow): instance = None # 类变量,用于在回调函数中引用当前实例 - + def __init__(self): super().__init__() MultiInstanceManager.instance = self # 保存当前实例的引用 self.setWindowTitle("铁鲨鱼多开自动化工具") self.setGeometry(100, 100, 530, 720) - + self.instances = {} self.starting_ports = set() # 正在启动中的端口集合,防止重复启动 self.init_ui() self.setup_logger() - + # 定时更新界面 self.timer = QTimer() self.timer.timeout.connect(self.update_display) self.timer.start(1000) - + # 崩溃检测定时器(每 5 秒检查一次) self.crash_detection_timer = QTimer() self.crash_detection_timer.timeout.connect(self.check_instances_crash) self.crash_detection_timer.start(5000) - + def init_ui(self): central_widget = QWidget() self.setCentralWidget(central_widget) layout = QVBoxLayout(central_widget) - + # 配置区域 settings_layout = QHBoxLayout() - + self.game_mode_combo = QComboBox() self.game_mode_combo.addItems(["单人", "30人"]) settings_layout.addWidget(QLabel("模式:")) settings_layout.addWidget(self.game_mode_combo) - + self.invest_check = QCheckBox("自动投资") self.invest_check.setChecked(False) settings_layout.addWidget(self.invest_check) settings_layout.addStretch() - + layout.addLayout(settings_layout) - + # 端口输入(横向,逗号分隔) ports_layout = QHBoxLayout() ports_layout.addWidget(QLabel("端口:")) @@ -346,7 +390,7 @@ def init_ui(self): pass ports_layout.addWidget(self.ports_input) layout.addLayout(ports_layout) - + # 按钮和端口选择器 btn_layout = QHBoxLayout() self.start_btn = QPushButton("全部启动") @@ -355,26 +399,26 @@ def init_ui(self): self.stop_btn.clicked.connect(self.stop_all) self.package_btn = QPushButton("打包数据") self.package_btn.clicked.connect(self.package_data) - + # 端口选择器,用于选择要查看哪个端口的日志 self.port_combo = QComboBox() self.port_combo.addItem("全部端口") self.port_combo.currentTextChanged.connect(self.update_log_filter) - + btn_layout.addWidget(self.start_btn) btn_layout.addWidget(self.stop_btn) btn_layout.addWidget(self.package_btn) btn_layout.addWidget(QLabel("日志过滤:")) btn_layout.addWidget(self.port_combo) layout.addLayout(btn_layout) - + font = QFont("Courier New", 10) if sys.platform == "win32": font = QFont("Consolas", 10) - + # 使用 QSplitter 让状态和日志区域可以自由拖动调整大小 splitter = QSplitter(Qt.Orientation.Vertical) - + # 状态显示区域(带单独控制按钮的滚动区域) self.status_scroll = QScrollArea() self.status_scroll.setWidgetResizable(True) @@ -387,23 +431,29 @@ def init_ui(self): self.status_scroll.setWidget(self.status_container) self.status_scroll.setMinimumHeight(80) splitter.addWidget(self.status_scroll) - + # 日志显示 self.log_display = LogDisplay() self.log_display.setFont(font) self.log_display.setMaximumBlockCount(2000) splitter.addWidget(self.log_display) - + splitter.setStretchFactor(0, 1) splitter.setStretchFactor(1, 3) splitter.setSizes([440, 330]) layout.addWidget(splitter) - + self.port_widgets = {} @staticmethod def _parse_ports(text): - parts = text.replace('\n', ',').replace(',', ',').replace(';', ',').replace(' ', ',').split(',') + parts = ( + text.replace("\n", ",") + .replace(",", ",") + .replace(";", ",") + .replace(" ", ",") + .split(",") + ) return [p.strip() for p in parts if p.strip().isdigit()] def start_all(self): @@ -412,13 +462,15 @@ def start_all(self): logger.info("保存端口配置到 multi_ports.txt") except Exception as e: logger.error(f"保存端口配置失败: {str(e)}") - + ports = self._parse_ports(self.ports_input.text()) game_mode = self.game_mode_combo.currentText() is_invest = self.invest_check.isChecked() - - logger.info(f"开始启动多开实例,端口列表: {ports}, 游戏模式: {game_mode}, 自动投资: {is_invest}") - + + logger.info( + f"开始启动多开实例,端口列表: {ports}, 游戏模式: {game_mode}, 自动投资: {is_invest}" + ) + def start_single_instance(port): # 检查是否已经在运行或正在启动中 is_running = False @@ -426,12 +478,12 @@ def start_single_instance(port): inst = self.instances[port] if inst.auto_fetch and inst.auto_fetch.auto_fetch_running: is_running = True - + # 使用启动锁防止重复启动 if port in self.starting_ports: logger.info(f"端口 {port} 的实例正在启动中,跳过重复启动") return - + if not is_running: # 添加到启动中集合 self.starting_ports.add(port) @@ -449,19 +501,21 @@ def start_single_instance(port): self.starting_ports.discard(port) else: logger.info(f"端口 {port} 的实例已经在运行,跳过启动") - + def start_all_instances(): threads = [] for port in ports: - t = threading.Thread(target=start_single_instance, args=(port,), daemon=True) + t = threading.Thread( + target=start_single_instance, args=(port,), daemon=True + ) t.start() threads.append(t) time.sleep(3) for t in threads: t.join() - + threading.Thread(target=start_all_instances, daemon=True).start() - + def stop_all(self): logger.info("开始停止所有实例") # 清空启动中集合,防止正在启动的实例继续运行 @@ -477,10 +531,16 @@ def package_data(self): zip_filename = data_package.package_data() if zip_filename and Path(zip_filename).exists(): # 在文件浏览器中高亮显示文件 - subprocess.run(f'explorer /select,"{Path(zip_filename).absolute()}"') - QMessageBox.information(self, "成功", f"数据已打包到 {zip_filename}") + subprocess.run( + f'explorer /select,"{Path(zip_filename).absolute()}"' + ) + QMessageBox.information( + self, "成功", f"数据已打包到 {zip_filename}" + ) else: - QMessageBox.warning(self, "警告", "没有找到可以打包的数据目录或打包失败。") + QMessageBox.warning( + self, "警告", "没有找到可以打包的数据目录或打包失败。" + ) except Exception as e: QMessageBox.critical(self, "错误", f"打包数据时发生错误: {str(e)}") @@ -489,43 +549,57 @@ class QTextEditLogger(logging.Handler): def __init__(self, text_edit): super().__init__() self.text_edit = text_edit - self.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')) + self.setFormatter( + logging.Formatter( + "%(asctime)s - %(levelname)s - %(message)s" + ) + ) self.target_port = None self.log_history = [] - + def set_target_port(self, port): self.target_port = port - + def emit(self, record): try: msg = self.format(record) except Exception: return - + self.log_history.append(msg) if len(self.log_history) > 2000: self.log_history.pop(0) - + if self.target_port: port_str = str(self.target_port) - if not (f"[127.0.0.1:{port_str}]" in msg or f"[{port_str}]" in msg or f"端口 {port_str}" in msg): + if not ( + f"[127.0.0.1:{port_str}]" in msg + or f"[{port_str}]" in msg + or f"端口 {port_str}" in msg + ): return - + if self.text_edit is not None: self.text_edit.append_log(msg) - + root_logger = logging.getLogger() self.text_edit_logger = QTextEditLogger(self.log_display) self.text_edit_logger.setLevel(logging.INFO) root_logger.addHandler(self.text_edit_logger) root_logger.setLevel(logging.INFO) - + # 确保子模块日志传播到 root logger - for name in ['login', 'auto_fetch', 'recognize', 'multi_instance', '__main__']: + for name in [ + "login", + "auto_fetch", + "recognize", + "multi_instance", + "__main__", + ]: child_logger = logging.getLogger(name) child_logger.setLevel(logging.INFO) child_logger.propagate = True - + def update_log_filter(self, text): if text == "全部端口": self.text_edit_logger.set_target_port(None) @@ -538,10 +612,11 @@ def update_log_filter(self, text): for msg in self.text_edit_logger.log_history: if f"[{text}]" in msg or f"端口 {text}" in msg or text in msg: self.log_display.append_log(msg) - + @staticmethod def _state_to_chinese(state): - from auto_fetch import GameState + from src.game.auto_fetch import GameState + state_map = { GameState.MAIN_MENU: "主页", GameState.MODE_SELECTION_UNSELECTED: "模式", @@ -553,36 +628,46 @@ def _state_to_chinese(state): GameState.UNKNOWN: "过场", } return state_map.get(state, "过场动画") - + def _create_port_widget(self, port): row = QHBoxLayout() row.setContentsMargins(4, 2, 4, 2) row.setSpacing(6) - + label = QLabel(port) label.setFixedWidth(60) - label.setFont(QFont("Consolas", 10) if sys.platform == "win32" else QFont("Courier New", 10)) + label.setFont( + QFont("Consolas", 10) + if sys.platform == "win32" + else QFont("Courier New", 10) + ) row.addWidget(label) - + status_label = QLabel("已停止") status_label.setFixedWidth(80) row.addWidget(status_label) - + detail_label = QLabel("") - detail_label.setFont(QFont("Consolas", 9) if sys.platform == "win32" else QFont("Courier New", 9)) + detail_label.setFont( + QFont("Consolas", 9) + if sys.platform == "win32" + else QFont("Courier New", 9) + ) row.addWidget(detail_label, 1) - + toggle_btn = QPushButton("启动") toggle_btn.setFixedWidth(50) - toggle_btn.clicked.connect(lambda checked, p=port: self._toggle_port(p)) + toggle_btn.clicked.connect( + lambda checked, p=port: self._toggle_port(p) + ) row.addWidget(toggle_btn) - + frame = QFrame() frame.setLayout(row) frame.setFrameShape(QFrame.Shape.StyledPanel) - + return frame, status_label, detail_label, toggle_btn - + def _toggle_port(self, port): if port in self.instances: instance = self.instances[port] @@ -594,7 +679,7 @@ def _toggle_port(self, port): else: game_mode = self.game_mode_combo.currentText() is_invest = self.invest_check.isChecked() - + def do_start(): if port in self.starting_ports: return @@ -605,57 +690,66 @@ def do_start(): instance.start(game_mode, is_invest) finally: self.starting_ports.discard(port) - + threading.Thread(target=do_start, daemon=True).start() - + def check_instances_crash(self): """检查实例是否崩溃,并尝试自动恢复""" current_time = time.time() - + # 遍历所有实例,检查崩溃情况 for port, instance in list(self.instances.items()): # 如果已经设置了停止事件,就不进行崩溃检测 if instance.stop_event.is_set(): continue - + # 检查活动时间(崩溃检测) inactive_time = current_time - instance.last_activity_time - + # 如果标记为运行但超过 3 分钟没有活动,视为崩溃 - if instance.status in ["正在运行", "连接中", "登录中"] and inactive_time > 180: - logger.warning(f"[{instance.serial}] 检测到无活动超过 {inactive_time:.0f} 秒,可能已崩溃,尝试重启") + if ( + instance.status in ["正在运行", "连接中", "登录中"] + and inactive_time > 180 + ): + logger.warning( + f"[{instance.serial}] 检测到无活动超过 {inactive_time:.0f} 秒,可能已崩溃,尝试重启" + ) self._restart_crashed_instance(port, instance) # 检查 auto_fetch 线程是否实际还在运行 elif instance.status == "正在运行" and instance.auto_fetch: if not instance.auto_fetch.auto_fetch_running: - logger.warning(f"[{instance.serial}] auto_fetch_running 为 False,可能已意外停止") + logger.warning( + f"[{instance.serial}] auto_fetch_running 为 False,可能已意外停止" + ) self._restart_crashed_instance(port, instance) - + def _restart_crashed_instance(self, port, instance): """重启崩溃的实例""" try: # 保存设置 game_mode = instance.game_mode if instance.game_mode else "30人" - is_invest = instance.is_invest if instance.is_invest is not None else False - + is_invest = ( + instance.is_invest if instance.is_invest is not None else False + ) + # 先停止(强制) instance.stop() - + # 从字典中清除 if port in self.instances: del self.instances[port] - + # 延迟重新启动 logger.info(f"[{instance.serial}] 准备重新启动...") - + def restart_task(): if port in self.starting_ports: logger.warning(f"端口 {port} 已在启动中,跳过重启") return - + logger.info(f"[{instance.serial}] 正在重新启动...") self.starting_ports.add(port) - + try: new_instance = DeviceInstance(port) self.instances[port] = new_instance @@ -666,98 +760,108 @@ def restart_task(): logger.error(f"[{instance.serial}] 崩溃后重启失败") finally: self.starting_ports.discard(port) - + threading.Thread(target=restart_task, daemon=True).start() except Exception as e: logger.error(f"重启崩溃实例时出错: {str(e)}") - + def update_display(self): input_ports = self._parse_ports(self.ports_input.text()) - - if not hasattr(self, '_last_input_ports'): + + if not hasattr(self, "_last_input_ports"): self._last_input_ports = [] - + ports_changed = input_ports != self._last_input_ports - + if ports_changed: current_text = self.port_combo.currentText() self.port_combo.clear() self.port_combo.addItem("全部端口") for port in input_ports: self.port_combo.addItem(port) - + if input_ports: if current_text in input_ports: self.port_combo.setCurrentText(current_text) else: self.port_combo.setCurrentIndex(1) self._last_input_ports = input_ports.copy() - + # 重建端口控件 for w in self.port_widgets.values(): - w['frame'].setParent(None) + w["frame"].setParent(None) self.port_widgets.clear() - + for i in range(self.status_layout.count() - 1): item = self.status_layout.itemAt(i) if item.widget(): item.widget().setParent(None) - + for port in input_ports: - frame, status_label, detail_label, toggle_btn = self._create_port_widget(port) + frame, status_label, detail_label, toggle_btn = ( + self._create_port_widget(port) + ) idx = self.status_layout.count() - 1 self.status_layout.insertWidget(idx, frame) self.port_widgets[port] = { - 'frame': frame, - 'status_label': status_label, - 'detail_label': detail_label, - 'toggle_btn': toggle_btn, + "frame": frame, + "status_label": status_label, + "detail_label": detail_label, + "toggle_btn": toggle_btn, } - + any_running = False for port in input_ports: if port not in self.port_widgets: continue - + widgets = self.port_widgets[port] - + if port in self.instances: instance = self.instances[port] - is_running = instance.auto_fetch and instance.auto_fetch.auto_fetch_running - + is_running = ( + instance.auto_fetch + and instance.auto_fetch.auto_fetch_running + ) + if is_running: any_running = True af = instance.auto_fetch - elapsed = time.time() - af.start_time if af.start_time else 0 + elapsed = ( + time.time() - af.start_time if af.start_time else 0 + ) hours, remainder = divmod(elapsed, 3600) minutes, _ = divmod(remainder, 60) - + state_name = "过场动画" - if hasattr(af, 'last_state') and af.last_state: + if hasattr(af, "last_state") and af.last_state: state_name = self._state_to_chinese(af.last_state) - - widgets['status_label'].setText(f"运行中·{state_name}") - widgets['detail_label'].setText( + + widgets["status_label"].setText(f"运行中·{state_name}") + widgets["detail_label"].setText( f"填写: {af.total_fill_count} | 错误: {af.incorrect_fill_count} | " f"预测: {af.current_prediction:.2f} | 时长: {int(hours)}h {int(minutes)}m" ) - widgets['toggle_btn'].setText("停止") + widgets["toggle_btn"].setText("停止") else: - widgets['status_label'].setText(instance.status) - widgets['detail_label'].setText("") + widgets["status_label"].setText(instance.status) + widgets["detail_label"].setText("") is_starting = instance.status in ("连接中", "登录中") - widgets['toggle_btn'].setText("停止" if is_starting else "启动") + widgets["toggle_btn"].setText( + "停止" if is_starting else "启动" + ) else: - widgets['status_label'].setText("已停止") - widgets['detail_label'].setText("") - widgets['toggle_btn'].setText("启动") - + widgets["status_label"].setText("已停止") + widgets["detail_label"].setText("") + widgets["toggle_btn"].setText("启动") + self.package_btn.setEnabled(not any_running) def closeEvent(self, event): self.stop_all() event.accept() + if __name__ == "__main__": app = QApplication(sys.argv) window = MultiInstanceManager() diff --git a/pyproject.toml b/pyproject.toml index 286692d..f7776a7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "CannotMax-Greenvine" -version = "1.0.8" +version = "2.0.0" description = "这是一个基于深度学习的明日方舟游戏辅助工具,用于自动识别游戏画面中的单位并预测战斗结果。" readme = "README.md" requires-python = ">=3.10" @@ -21,7 +21,7 @@ dependencies = [ ] [dependency-groups] -dev = ["onnx>=1.19.0", "onnxscript>=0.7.0", "pyinstaller>=6.15.0"] +dev = ["onnx>=1.19.0", "onnxscript>=0.7.0", "types-toml", "pandas-stubs", "pyinstaller>=6.15.0"] [project.optional-dependencies] cpu = ["torch", "torchvision"] diff --git a/simulator/.gitignore b/simulator/.gitignore deleted file mode 100644 index 763624e..0000000 --- a/simulator/.gitignore +++ /dev/null @@ -1 +0,0 @@ -__pycache__/* \ No newline at end of file diff --git a/simulator/scene.json b/simulator/scene.json deleted file mode 100644 index 39a51eb..0000000 --- a/simulator/scene.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "left": - [ - { "name": "酸液源石虫·α", "count": 0 }, - { "name": "高能源石虫", "count": 0 }, - { "name": "阿咬", "count": 0 }, - { "name": "狂暴的猎狗pro", "count": 0 }, - { "name": "提亚卡乌好战者", "count": 0 }, - { "name": "污染躯壳", "count": 0 }, - { "name": "大盾哥", "count": 0 }, - { "name": "宿主流浪者", "count": 0 }, - { "name": "光剑", "count": 4 }, - { "name": "泥岩巨像", "count": 0 }, - { "name": "呼啸骑士团学徒", "count": 0 }, - { "name": "萨卡兹大剑手", "count": 0 }, - { "name": "狂暴宿主组长", "count": 0 }, - { "name": "海螺", "count": 0 }, - { "name": "拳手囚犯", "count": 0 }, - { "name": "高塔术师", "count": 0 }, - { "name": "冰原术师", "count": 0 }, - { "name": "矿脉守卫", "count": 0 }, - { "name": "“庞贝”", "count": 0 }, - { "name": "绵羊", "count": 0 }, - { "name": "食腐狗", "count": 0 }, - { "name": "温顺的武装驮兽", "count": 3 }, - { "name": "鼠鼠", "count": 4 }, - { "name": "“投石机”", "count": 0 }, - { "name": "船长", "count": 0 } - ], - "right": - [ - { "name": "酸液源石虫·α", "count": 0 }, - { "name": "高能源石虫", "count": 0 }, - { "name": "阿咬", "count": 0 }, - { "name": "狂暴的猎狗pro", "count": 0 }, - { "name": "提亚卡乌好战者", "count": 0 }, - { "name": "污染躯壳", "count": 0 }, - { "name": "大盾哥", "count": 0 }, - { "name": "宿主流浪者", "count": 0 }, - { "name": "光剑", "count": 0 }, - { "name": "泥岩巨像", "count": 0 }, - { "name": "呼啸骑士团学徒", "count": 0 }, - { "name": "萨卡兹大剑手", "count": 0 }, - { "name": "狂暴宿主组长", "count": 0 }, - { "name": "海螺", "count": 0 }, - { "name": "拳手囚犯", "count": 11 }, - { "name": "高塔术师", "count": 0 }, - { "name": "冰原术师", "count": 0 }, - { "name": "矿脉守卫", "count": 0 }, - { "name": "“庞贝”", "count": 3 }, - { "name": "绵羊", "count": 9 }, - { "name": "食腐狗", "count": 0 }, - { "name": "温顺的武装驮兽", "count": 0 }, - { "name": "鼠鼠", "count": 0 }, - { "name": "“投石机”", "count": 0 }, - { "name": "船长", "count": 0 } - ] -} \ No newline at end of file diff --git a/simulator/stats.py b/simulator/stats.py deleted file mode 100644 index 95becc6..0000000 --- a/simulator/stats.py +++ /dev/null @@ -1,172 +0,0 @@ -import json -from collections import defaultdict -import numpy as np -import pandas as pd -from scipy.stats import fisher_exact -import matplotlib.pyplot as plt - -# 配置中文字体 -plt.rcParams['font.sans-serif'] = ['Microsoft YaHei'] -plt.rcParams['axes.unicode_minus'] = False - - -def calculate_significance(row): - """计算修正后的统计显著性""" - # 关键参数 - error_total = row['under_estimate'] + row['over_estimate'] # 错误样本总次数 - correct_total = row['total'] - error_total # 正确样本总次数 - - # 过滤无效数据 - if error_total == 0 or correct_total == 0: - return np.nan - - # 低估检验(单侧) - contingency_under = [ - [row['under_estimate'], row['over_estimate']], - [0, correct_total] - ] - _, under_p = fisher_exact(contingency_under, alternative='greater') - - # 高估检验(单侧) - contingency_over = [ - [row['over_estimate'], row['under_estimate']], - [0, correct_total] - ] - _, over_p = fisher_exact(contingency_over, alternative='greater') - - return under_p + over_p - -def analyze_monster_balance(data_path): - # 初始化统计容器 - stats = defaultdict(lambda: { - 'total': 0, # 总出现次数 - 'error': 0, # 错误预测中出现次数 - 'under_estimate': 0, # 低估计数(实际应胜但预测失败) - 'over_estimate': 0 # 高估计数(实际应败但预测成功) - }) - - # 数据加载与分析 - with open(data_path, encoding='utf-8') as f: - for line in f: - record = json.loads(line.strip()) - actual = record['result'] - pred = record['pred'] - - for side in ["left", "right"]: - for monster, count in record[side].items(): - # 基础统计 - stats[monster]['total'] += 1 - - # 仅统计预测错误的情况 - if pred != actual: - stats[monster]['error'] += 1 - - # 判断估计方向 - if (side == actual): - stats[monster]['under_estimate'] += 1 - elif (side != actual): - stats[monster]['over_estimate'] += 1 - - # 构建分析数据框 - df = pd.DataFrame.from_dict(stats, orient='index') - df = df[df['total'] > 0] # 过滤无效数据 - - # 计算统计指标 - df['error_rate'] = df['error'] / df['total'] - df['bias_score'] = (df['under_estimate'] - df['over_estimate']) / df['total'] - df['abs_bias'] = abs(df['bias_score']) - - # # Fisher精确检验 - # def apply_fisher(row): - # contingency = [ - # [row['under_estimate'], row['over_estimate']], - # [row['total'] - row['under_estimate'], - # row['total'] - row['over_estimate']] - # ] - # _, p_value = fisher_exact(contingency) - # return p_value - - df['p_value'] = df.apply(calculate_significance, axis=1) - - # 筛选显著结果 (p<0.04 且总出现次数>1) - valid = df[(df['p_value'] < 0.04) & (df['total'] >= 1)] - - # 生成可视化 - plot_monster_bias(valid) - - return valid.sort_values('abs_bias', ascending=False) - -def plot_monster_bias(df): - plt.figure(figsize=(12, 8)) - - # 设置颜色映射 - colors = np.where(df['bias_score'] > 0, - '#4C72B0', # 蓝色表示低估 - '#DD8452') # 橙色表示高估 - - # 绘制条形图 - bars = plt.barh(df.index, df['bias_score'], color=colors) - - # 添加统计标注 - for bar, (_, row) in zip(bars, df.iterrows()): - plt.text(bar.get_width() + 0.02, - bar.get_y() + bar.get_height()/2, - f"p={row['p_value']:.3f}\nN={row['total']}", - va='center') - - # 图表装饰 - plt.axvline(0, color='gray', linestyle='--') - plt.title('怪物数值平衡性分析', fontsize=14) - plt.xlabel('偏差分数(正值表示低估,负值表示高估)') - plt.ylabel('怪物名称') - plt.xlim(-1.1, 1.1) - plt.grid(axis='x', alpha=0.3) - plt.tight_layout() - plt.savefig('monster_balance.png', dpi=300) - plt.close() - -# 执行分析 -if __name__ == "__main__": - result_df = analyze_monster_balance("../errors.json") - - # 打印格式化结果 - print("="*60) - print(f"{'怪物名称':<10}\t{'偏差方向':<8}\t{'偏差分数':<8}\t{'p值':<10}\t{'样本量'}") - print("-"*60) - for name, row in result_df.iterrows(): - direction = "低估" if row['bias_score'] > 0 else "高估" - print(f"{name:<10}\t{direction:<8}\t{row['bias_score']:.2f}{'*' if row['p_value']<0.01 else '':<4}" - f"\t{row['p_value']:.3f}\t{'':<6}\t{row['total']}") - - plot_monster_bias(result_df) - -with open("../errors.json", encoding='utf-8') as f: - data = f.read() - -counter = defaultdict(int) -power_counter = defaultdict(int) -err_counter = defaultdict(int) - -for line in data.strip().split('\n'): - record = json.loads(line) - result = record['result'] - pred = record['pred'] - for side in ["left", "right"]: - for monster, count in record[side].items(): - counter[monster] += 1 - if pred != result: - if side == result: - # 应该获胜却没有获胜,有低估的可能性 - power_counter[monster] += 1 - else: - # 不该获胜的获胜了,有高估的可能性 - power_counter[monster] -= 1 - err_counter[monster] += 1 - -sorted_monsters = sorted(err_counter.items(), key=lambda x: (-x[1] / counter[x[0]], x[0])) - -for monster, count in sorted_monsters: - str = "低估" if power_counter[monster] > 0 else "高估" - total = counter[monster] - err_total = count - print(f"{monster}: {err_total}/{total} = {err_total/total} 估计值:【{str}】{-power_counter[monster] / total * 100}") \ No newline at end of file diff --git a/specialmonster.py b/specialmonster.py deleted file mode 100644 index 708cd64..0000000 --- a/specialmonster.py +++ /dev/null @@ -1,37 +0,0 @@ -class SpecialMonsterHandler: - """ - 可按照格式自行添加特殊怪物的留言信息: - 格式: - 怪物ID: { - 'name': '怪物名称', # 可留空 - 'win_message': '胜利时显示的消息', # 可留空 - 'lose_message': '失败时显示的消息', # 可留空 - } - """ - def __init__(self): - self.special_monsters = { - 1: { - 'name': '狗神', - 'win_message': "全军出击,我咬死你!", - 'lose_message': "牙崩了牙崩了" - }, - } - - def check_special_monsters(self, left_monsters, right_monsters, winner): - messages = [] - - for monster_id, config in self.special_monsters.items(): - left_has = left_monsters[str(monster_id)].text().isdigit() and int(left_monsters[str(monster_id)].text()) > 0 - right_has = right_monsters[str(monster_id)].text().isdigit() and int(right_monsters[str(monster_id)].text()) > 0 - - if left_has or right_has: - if winner == "左方" and left_has and config['win_message']: - messages.append(config['win_message']) - elif winner == "右方" and right_has and config['win_message']: - messages.append(config['win_message']) - elif winner == "右方" and left_has and config['lose_message']: - messages.append(config['lose_message']) - elif winner == "左方" and right_has and config['lose_message']: - messages.append(config['lose_message']) - - return "\n".join(messages) \ No newline at end of file diff --git a/models/__init__.py b/src/__init__.py similarity index 100% rename from models/__init__.py rename to src/__init__.py diff --git a/simulator/__init__.py b/src/analysis/__init__.py similarity index 100% rename from simulator/__init__.py rename to src/analysis/__init__.py diff --git a/sim_mc.py b/src/analysis/sim_mc.py similarity index 67% rename from sim_mc.py rename to src/analysis/sim_mc.py index df80f9e..c1efc7c 100644 --- a/sim_mc.py +++ b/src/analysis/sim_mc.py @@ -1,25 +1,33 @@ -from collections import Counter, defaultdict +from collections import Counter from concurrent.futures import ThreadPoolExecutor +from pathlib import Path import queue import threading -import time import os import copy from enum import Enum, auto import tkinter as tk + # from tkinter import messagebox # messagebox 已被自定义提示替代,可以注释或移除 from PIL import Image, ImageTk -import math -from simulator.battle_field import Battlefield # 确保 Battlefield 已导入 -from simulator.monsters import MonsterFactory # 确保 MonsterFactory 已导入 -from simulator.utils import MONSTER_MAPPING, REVERSE_MONSTER_MAPPING, Faction # 根据你的实际路径调整 -from simulator.vector2d import FastVector # 确保 FastVector 已导入 -from unit import Unit # 确保 Unit 已导入 -import json # REMOVED_TEAM_INTERFACE: Added missing import for the main block -import random # REMOVED_TEAM_INTERFACE: Added missing import for the main block -import sys # Import sys for stdin -from simulator.monsters import AttackState, Monster, MonsterFactory -from recognize import MONSTER_COUNT + +import json +import random +import sys + + + +if __name__ == "__main__": + sys.path.insert(0, str(Path(__file__).parent.parent.parent)) + from src.simulation.battle_field import Battlefield + from src.core.config import MONSTER_DATA, MONSTER_IMAGES, MONSTER_COUNT + from src.core.paths import DATA_DIR, PROJECT_ROOT, simulation_path + from src.simulation.utils import Faction +else: + from ..simulation.battle_field import Battlefield + from ..core.config import MONSTER_DATA, MONSTER_IMAGES, MONSTER_COUNT + from ..core.paths import DATA_DIR, PROJECT_ROOT, simulation_path + from ..simulation.utils import Faction class AppState(Enum): @@ -38,11 +46,19 @@ def __init__(self, ui_update_callback): def transition_to(self, new_state): """状态转换并触发UI更新""" allowed_transitions = { - AppState.INITIAL: [AppState.INITIAL, AppState.SETUP, AppState.ENDED], - AppState.SETUP: [AppState.INITIAL, AppState.SETUP, AppState.SIMULATING], + AppState.INITIAL: [ + AppState.INITIAL, + AppState.SETUP, + AppState.ENDED, + ], + AppState.SETUP: [ + AppState.INITIAL, + AppState.SETUP, + AppState.SIMULATING, + ], AppState.SIMULATING: [AppState.PAUSED, AppState.ENDED], AppState.PAUSED: [AppState.SIMULATING, AppState.SETUP], - AppState.ENDED: [AppState.INITIAL, AppState.SETUP] + AppState.ENDED: [AppState.INITIAL, AppState.SETUP], } if new_state in allowed_transitions[self.state]: @@ -54,47 +70,55 @@ def transition_to(self, new_state): def get_control_states(self): """返回各控件的状态字典""" states = { - 'deploy': {'state': tk.NORMAL, 'text': '部署怪物'}, - 'confirm_start': {'state': tk.DISABLED}, - 'pause': {'state': tk.DISABLED, 'text': '暂停'}, - 'restore': {'state': tk.DISABLED}, - 'speed_entry': {'state': tk.NORMAL}, - 'clear': {'state': tk.NORMAL}, - 'timer': {'text': ''} + "deploy": {"state": tk.NORMAL, "text": "部署怪物"}, + "confirm_start": {"state": tk.DISABLED}, + "pause": {"state": tk.DISABLED, "text": "暂停"}, + "restore": {"state": tk.DISABLED}, + "speed_entry": {"state": tk.NORMAL}, + "clear": {"state": tk.NORMAL}, + "timer": {"text": ""}, } if self.state == AppState.INITIAL: pass elif self.state == AppState.SETUP: - states.update({ - 'deploy': {'state': tk.NORMAL, 'text': '重新部署'}, - 'confirm_start': {'state': tk.NORMAL}, - 'timer': {'text': '未开始'} - }) + states.update( + { + "deploy": {"state": tk.NORMAL, "text": "重新部署"}, + "confirm_start": {"state": tk.NORMAL}, + "timer": {"text": "未开始"}, + } + ) elif self.state == AppState.SIMULATING: - states.update({ - 'deploy': {'state': tk.DISABLED, 'text': '重新部署'}, - 'confirm_start': {'state': tk.DISABLED}, - 'pause': {'state': tk.NORMAL, 'text': '暂停'}, - 'clear': {'state': tk.DISABLED}, - }) + states.update( + { + "deploy": {"state": tk.DISABLED, "text": "重新部署"}, + "confirm_start": {"state": tk.DISABLED}, + "pause": {"state": tk.NORMAL, "text": "暂停"}, + "clear": {"state": tk.DISABLED}, + } + ) elif self.state == AppState.PAUSED: - states.update({ - 'restore': {'state': tk.NORMAL}, - 'pause': {'state': tk.NORMAL, 'text': '继续'}, - 'deploy': {'state': tk.NORMAL, 'text': '重新部署'} - }) + states.update( + { + "restore": {"state": tk.NORMAL}, + "pause": {"state": tk.NORMAL, "text": "继续"}, + "deploy": {"state": tk.NORMAL, "text": "重新部署"}, + } + ) elif self.state == AppState.ENDED: - states.update({ - 'deploy': {'state': tk.DISABLED, 'text': '重新部署'}, - 'restore': {'state': tk.NORMAL}, - 'pause': {'state': tk.DISABLED, 'text': '暂停'}, - 'timer': {'text': '战斗结束'} - }) + states.update( + { + "deploy": {"state": tk.DISABLED, "text": "重新部署"}, + "restore": {"state": tk.NORMAL}, + "pause": {"state": tk.DISABLED, "text": "暂停"}, + "timer": {"text": "战斗结束"}, + } + ) return states @@ -145,6 +169,16 @@ def __init__(self, master: tk.Tk, battle_data): self.state_machine.transition_to(AppState.INITIAL) self.enter_setup_phase() + def _resolve_monster_icon_key(self, monster_name: str) -> str: + """根据怪物名称解析图标文件名。""" + if monster_name in self.monster_icon_key_map: + return self.monster_icon_key_map[monster_name] + + if monster_name in MONSTER_IMAGES: + return monster_name + + return self.default_icon_key + def update_ui_state(self): """根据当前状态更新所有控件状态""" states = self.state_machine.get_control_states() @@ -157,8 +191,10 @@ def hide_window(self): def load_assets(self): self.icons = {} + self.monster_icon_key_map = {} + self.default_icon_key = "empty" if "empty" in MONSTER_IMAGES else "" try: - with open("simulator/monsters.json", encoding='utf-8') as f: + with open(simulation_path("monsters.json"), encoding="utf-8") as f: self.monster_data = json.load(f)["monsters"] except FileNotFoundError: print("错误: monsters.json 未找到,请检查路径!") @@ -166,21 +202,47 @@ def load_assets(self): return - for i in range(self.num_monsters): - image_file_id = i + 1 + # 建立“名称/原始名称 -> 图标文件名”的兼容映射。 + for _, row in MONSTER_DATA.iterrows(): + monster_name = str(row["名称"]) + original_name = str(row["原始名称"]) + + icon_key = None + if original_name in MONSTER_IMAGES: + icon_key = original_name + elif monster_name in MONSTER_IMAGES: + icon_key = monster_name + + if icon_key is None: + icon_key = self.default_icon_key + + self.monster_icon_key_map[monster_name] = icon_key + self.monster_icon_key_map[original_name] = icon_key + + # 只为实际存在的图片创建 Tk 图像,避免访问旧的数字编号路径。 + for image_key, image_array in MONSTER_IMAGES.items(): try: - image = Image.open(f'images/{image_file_id}.png') - self.icons[i] = { - "red": ImageTk.PhotoImage(image.resize((40, 40))), - "blue": ImageTk.PhotoImage(image.resize((40, 40)).transpose(Image.FLIP_LEFT_RIGHT)) + if image_array is None: + raise ValueError("图像数据为空") + + image = Image.fromarray(image_array[:, :, ::-1]) + image_40 = image.resize((40, 40)) + self.icons[image_key] = { + "red": ImageTk.PhotoImage(image_40, master=self.master), + "blue": ImageTk.PhotoImage( + image_40.transpose(Image.FLIP_LEFT_RIGHT), + master=self.master, + ), } except Exception as e: - # 同样,show_message_below_button 可能还不可用 - print(f"加载图标错误 (图标键: {i}, 文件名ID: {image_file_id}): {str(e)}") - self.icons[i] = { - "red": ImageTk.PhotoImage(Image.new("RGB", (40, 40), "gray")), - "blue": ImageTk.PhotoImage(Image.new("RGB", (40, 40), "gray")) - } + print(f"加载图标错误 (图标键: {image_key}): {str(e)}") + + if self.default_icon_key and self.default_icon_key not in self.icons: + fallback = Image.new("RGB", (40, 40), "gray") + self.icons[self.default_icon_key] = { + "red": ImageTk.PhotoImage(fallback, master=self.master), + "blue": ImageTk.PhotoImage(fallback, master=self.master), + } def init_battlefield_for_setup(self): self.state_machine.transition_to(AppState.SETUP) @@ -191,7 +253,8 @@ def init_battlefield_for_setup(self): right_army_config = self.battle_data.get("right", {}) self.battle_field.setup_battle( - left_army_config, right_army_config, self.monster_data) + left_army_config, right_army_config, self.monster_data + ) while self.battle_field.gameTime < 6.0: result = self.battle_field.run_one_frame() if result: @@ -199,7 +262,8 @@ def init_battlefield_for_setup(self): def _normalize_outcome(self, outcome): try: - from simulator.utils import Faction + from src.simulation.utils import Faction + if outcome == Faction.LEFT: return "LEFT" if outcome == Faction.RIGHT: @@ -222,7 +286,9 @@ def create_widgets(self): self.message_label.pack(pady=2, anchor="w") # ====== 蒙特卡洛控制区 ====== - mc_frame = tk.LabelFrame(self.master, text="蒙特卡洛模拟", padx=10, pady=8) + mc_frame = tk.LabelFrame( + self.master, text="蒙特卡洛模拟", padx=10, pady=8 + ) mc_frame.pack(fill=tk.X, padx=10, pady=8) tk.Label(mc_frame, text="模拟次数:").grid(row=0, column=0, sticky="w") @@ -232,36 +298,47 @@ def create_widgets(self): self.mc_seed_var = tk.StringVar(value="") # 可选随机种子 tk.Label(mc_frame, text="随机种子(可空):").grid( - row=0, column=2, sticky="w", padx=(20, 0)) + row=0, column=2, sticky="w", padx=(20, 0) + ) self.mc_seed_entry = tk.Entry( - mc_frame, width=12, textvariable=self.mc_seed_var) + mc_frame, width=12, textvariable=self.mc_seed_var + ) self.mc_seed_entry.grid(row=0, column=3, sticky="w", padx=6) self.mc_run_button = tk.Button( - mc_frame, text="运行蒙卡", command=self.start_monte_carlo_threads) + mc_frame, text="运行蒙卡", command=self.start_monte_carlo_threads + ) self.mc_run_button.grid(row=0, column=4, padx=12) self.mc_stop_button = tk.Button( - mc_frame, text="停止", state=tk.DISABLED, command=self.stop_monte_carlo_threads) + mc_frame, + text="停止", + state=tk.DISABLED, + command=self.stop_monte_carlo_threads, + ) self.mc_stop_button.grid(row=0, column=5, padx=4) # ... self.mc_canvas = tk.Canvas(mc_frame, height=160, bg="white") - self.mc_canvas.grid(row=2, column=0, columnspan=6, - sticky="we", pady=(6, 2)) + self.mc_canvas.grid( + row=2, column=0, columnspan=6, sticky="we", pady=(6, 2) + ) self.mc_canvas.bind( - "", lambda e: self._redraw_mc_canvas()) # 动态重绘 + "", lambda e: self._redraw_mc_canvas() + ) # 动态重绘 # 结果文字 self.mc_result_text = tk.Text(mc_frame, height=6, width=70) self.mc_result_text.grid( - row=1, column=0, columnspan=5, pady=(8, 4), sticky="we") + row=1, column=0, columnspan=5, pady=(8, 4), sticky="we" + ) self.mc_result_text.configure(state=tk.DISABLED) # 简单条形图画布(显示胜负/平局分布) self.mc_canvas = tk.Canvas(mc_frame, height=160, bg="white") - self.mc_canvas.grid(row=2, column=0, columnspan=5, - sticky="we", pady=(6, 2)) + self.mc_canvas.grid( + row=2, column=0, columnspan=5, sticky="we", pady=(6, 2) + ) self.canvas = None def run_single_battle(self, left_cfg, right_cfg): @@ -282,7 +359,8 @@ def run_single_battle(self, left_cfg, right_cfg): outcome = "DRAW" else: try: - from simulator.utils import Faction + from src.simulation.utils import Faction + if result == Faction.LEFT: outcome = "LEFT" elif result == Faction.RIGHT: @@ -326,13 +404,17 @@ def start_monte_carlo_threads(self): try: seed_val = int(seed_txt) except ValueError: - self.show_message_below_button("随机种子需为整数或留空", is_error=True) + self.show_message_below_button( + "随机种子需为整数或留空", is_error=True + ) return - left_cfg = copy.deepcopy(self.battle_data.get("left", {})) + left_cfg = copy.deepcopy(self.battle_data.get("left", {})) right_cfg = copy.deepcopy(self.battle_data.get("right", {})) if not left_cfg or not right_cfg: - self.show_message_below_button("错误:左右双方任一为空,无法进行蒙卡", is_error=True) + self.show_message_below_button( + "错误:左右双方任一为空,无法进行蒙卡", is_error=True + ) return # 状态初始化 @@ -359,16 +441,21 @@ def start_monte_carlo_threads(self): # 关键:制造足够多的小任务(比如每个任务 10~50 次) # 这样就能频繁完成并推送结果,界面“按完成块”刷新 - TARGET_TASKS = min(max(32, workers * 16), runs) # 至少几十个分片 + TARGET_TASKS = min(max(32, workers * 16), runs) # 至少几十个分片 chunk = max(1, runs // TARGET_TASKS) remain = runs idx = 0 while remain > 0: n = min(chunk, remain) - fut = self.executor.submit(self._mc_task, n, left_cfg, right_cfg, - None if seed_val is None else seed_val + idx, - self.mc_stop_event) + fut = self.executor.submit( + self._mc_task, + n, + left_cfg, + right_cfg, + None if seed_val is None else seed_val + idx, + self.mc_stop_event, + ) # 回调里**不要**动 UI;只把结果丢进队列 def _on_done(f): @@ -451,10 +538,13 @@ def _mc_finish(self): self.show_message_below_button("蒙卡完成", is_error=False) else: self.show_message_below_button( - f"已停止(完成 {done}/{target} 次)", is_error=False) + f"已停止(完成 {done}/{target} 次)", is_error=False + ) self.mc_stop_event.clear() - def _render_mc_text_and_chart(self, counts: Counter, done: int, target: int): + def _render_mc_text_and_chart( + self, counts: Counter, done: int, target: int + ): left_win = counts.get("LEFT", 0) right_win = counts.get("RIGHT", 0) draw_cnt = counts.get("DRAW", 0) @@ -476,7 +566,11 @@ def _render_mc_text_and_chart(self, counts: Counter, done: int, target: int): self.mc_result_text.configure(state=tk.DISABLED) # 图 - items = [("左方胜", left_win), ("右方胜", right_win), ("平局", draw_cnt)] + items = [ + ("左方胜", left_win), + ("右方胜", right_win), + ("平局", draw_cnt), + ] self.draw_mc_barchart(items, total=max(1, done)) # 避免除零 def _redraw_mc_canvas(self): @@ -484,9 +578,11 @@ def _redraw_mc_canvas(self): with self.mc_lock: counts = Counter(self._mc_counts) done = self._mc_total_done - items = [("左方胜", counts.get("LEFT", 0)), - ("右方胜", counts.get("RIGHT", 0)), - ("平局", counts.get("DRAW", 0))] + items = [ + ("左方胜", counts.get("LEFT", 0)), + ("右方胜", counts.get("RIGHT", 0)), + ("平局", counts.get("DRAW", 0)), + ] self.draw_mc_barchart(items, total=max(1, done)) def draw_mc_barchart(self, items, total): @@ -503,44 +599,52 @@ def draw_mc_barchart(self, items, total): H = int(c.winfo_height()) or 160 pad_x = 40 pad_y = 24 - bar_space = (W - 2*pad_x) + bar_space = W - 2 * pad_x bar_w = bar_space // max(1, len(items)) - 16 max_cnt = max(x[1] for x in items) or 1 # 坐标轴 - c.create_line(pad_x, H - pad_y, W - pad_x//2, H - pad_y) # x轴 - c.create_line(pad_x, pad_y//2, pad_x, H - pad_y) # y轴 + c.create_line(pad_x, H - pad_y, W - pad_x // 2, H - pad_y) # x轴 + c.create_line(pad_x, pad_y // 2, pad_x, H - pad_y) # y轴 for i, (label, cnt) in enumerate(items): x0 = pad_x + i * (bar_w + 16) + 8 x1 = x0 + bar_w # 高度按计数线性映射 h_ratio = cnt / max_cnt if max_cnt > 0 else 0.0 - bar_h = int((H - 2*pad_y) * h_ratio) + bar_h = int((H - 2 * pad_y) * h_ratio) y0 = H - pad_y - bar_h y1 = H - pad_y # 绘制条 c.create_rectangle(x0, y0, x1, y1, fill="#6aa84f", outline="") # 文本:计数与百分比 pct = f"{(cnt/total):.1%}" - c.create_text((x0+x1)//2, y0-10, text=f"{cnt} ({pct})", anchor="s") + c.create_text( + (x0 + x1) // 2, y0 - 10, text=f"{cnt} ({pct})", anchor="s" + ) # 类别名 - c.create_text((x0+x1)//2, H - pad_y + 12, text=label, anchor="n") + c.create_text( + (x0 + x1) // 2, H - pad_y + 12, text=label, anchor="n" + ) def refresh_canvas_display(self): if self.monte_carlo_mode: return # ... 原本的画网格/单位/连线/计时 等逻辑保持不变 - def show_message_below_button(self, message, is_error=False, duration=5000): + def show_message_below_button( + self, message, is_error=False, duration=5000 + ): """在按钮下方显示文字提示,并在指定时间后消失""" if self.message_label: self.message_label.config( - text=message, fg="red" if is_error else "black") + text=message, fg="red" if is_error else "black" + ) if self.message_timer_id: self.master.after_cancel(self.message_timer_id) self.message_timer_id = self.master.after( - duration, self.clear_message_below_button) + duration, self.clear_message_below_button + ) def clear_message_below_button(self): """清除提示信息""" @@ -561,10 +665,13 @@ def main(): root = tk.Tk() # root.withdraw() # 如果不需要立即隐藏主窗口,可以注释掉 - initial_battle_setup = {"left": {"炮击组长": 3, "“庞贝”": 2}, "right": {"沸血骑士团精锐": 6, "炽焰源石虫": 23}, - "result": "left"} + initial_battle_setup = { + "left": {"炮击组长": 3, "“庞贝”": 2}, + "right": {"沸血骑士团精锐": 6, "炽焰源石虫": 23}, + "result": "left", + } - sys.stdin.reconfigure(encoding='utf-8') + sys.stdin.reconfigure(encoding="utf-8") try: if not sys.stdin.isatty(): json_data = sys.stdin.read() @@ -576,7 +683,9 @@ def main(): else: print("stdin 是交互式终端,使用默认配置。") except json.JSONDecodeError: - print("错误: 无法解析 stdin 中的 JSON 数据,请检查格式,将使用默认配置。") + print( + "错误: 无法解析 stdin 中的 JSON 数据,请检查格式,将使用默认配置。" + ) except Exception as e: print(f"从 stdin 读取或解析时发生未知错误: {e},将使用默认配置。") @@ -587,24 +696,37 @@ def main(): if not app.monster_data: # 检查 monsters.json 是否加载成功 error_messages.append("错误: monsters.json 未找到或为空,请检查文件!") - if len(initial_battle_setup.get("left", {})) == 0 or len(initial_battle_setup.get("right", {})) == 0: + if ( + len(initial_battle_setup.get("left", {})) == 0 + or len(initial_battle_setup.get("right", {})) == 0 + ): error_messages.append("错误:至少一方的怪物列表为空!") problematic_monsters_found = [] - problematic_monster_names = ["矿脉守卫", "提亚卡乌好战者", "凋零萨卡兹", "狂暴宿主组长", "高能源石虫"] + problematic_monster_names = [ + "矿脉守卫", + "提亚卡乌好战者", + "凋零萨卡兹", + "狂暴宿主组长", + "高能源石虫", + ] for team_key in ["left", "right"]: for monster_name in initial_battle_setup.get(team_key, {}): if monster_name in problematic_monster_names: problematic_monsters_found.append( - f"{('左方' if team_key == 'left' else '右方')}存在问题怪物: {monster_name}") + f"{('左方' if team_key == 'left' else '右方')}存在问题怪物: {monster_name}" + ) if problematic_monsters_found: - error_messages.append("警告: " + "; ".join(problematic_monsters_found)) + error_messages.append( + "警告: " + "; ".join(problematic_monsters_found) + ) if error_messages: - app.show_message_below_button(" | ".join( - error_messages), is_error=True, duration=10000) + app.show_message_below_button( + " | ".join(error_messages), is_error=True, duration=10000 + ) if "monsters.json 未找到或为空" in " | ".join(error_messages): print("由于 monsters.json 缺失或错误,模拟器可能无法正常工作。") diff --git a/similar_history_match.py b/src/analysis/similar_history_match.py similarity index 74% rename from similar_history_match.py rename to src/analysis/similar_history_match.py index ea9f1ff..286194f 100644 --- a/similar_history_match.py +++ b/src/analysis/similar_history_match.py @@ -1,7 +1,9 @@ import numpy as np import pandas as pd -from config import MONSTER_COUNT -from config import FIELD_FEATURE_COUNT +from src.core.config import MONSTER_COUNT +from src.core.config import FIELD_FEATURE_COUNT +from src.core.paths import PROJECT_ROOT + def cosine_similarity_manual(a, b): """手动实现余弦相似度,替代 sklearn 以减小打包体积""" @@ -13,10 +15,11 @@ def cosine_similarity_manual(a, b): dot = np.dot(a, b.T) return dot / (norm_a * norm_b.T) + class HistoryMatch: """错题本数据集的读取和处理类""" - def __init__(self, csv_path="arknights.csv"): + def __init__(self, csv_path=PROJECT_ROOT / "data" / "arknights.csv"): # 初始化时加载历史对局数据 self.csv_path = csv_path self.load_history_data() @@ -29,43 +32,66 @@ def load_history_data(self): """读取 CSV 文件,加载历史对局的左右阵容、地形及胜负标签""" try: df = pd.read_csv(self.csv_path, header=None, skiprows=1) - + # 新数据格式: [怪物L(77), 场地L(6), 怪物R(77), 场地R(6), Result, ImgPath] total_features = (MONSTER_COUNT + FIELD_FEATURE_COUNT) * 2 - + if df.shape[1] >= total_features + 1: # 至少包含特征和结果列 # 提取各部分特征 left_monster_end = MONSTER_COUNT left_field_end = MONSTER_COUNT + FIELD_FEATURE_COUNT - right_monster_end = MONSTER_COUNT + FIELD_FEATURE_COUNT + MONSTER_COUNT - right_field_end = MONSTER_COUNT + FIELD_FEATURE_COUNT + MONSTER_COUNT + FIELD_FEATURE_COUNT - + right_monster_end = ( + MONSTER_COUNT + FIELD_FEATURE_COUNT + MONSTER_COUNT + ) + right_field_end = ( + MONSTER_COUNT + + FIELD_FEATURE_COUNT + + MONSTER_COUNT + + FIELD_FEATURE_COUNT + ) + # 分别提取怪物和地形特征 - left_monsters = df.iloc[:, 0:left_monster_end].values.astype(float) - left_terrain = df.iloc[:, left_monster_end:left_field_end].values.astype(float) - right_monsters = df.iloc[:, left_field_end:right_monster_end].values.astype(float) - right_terrain = df.iloc[:, right_monster_end:right_field_end].values.astype(float) - + left_monsters = df.iloc[:, 0:left_monster_end].values.astype( + float + ) + left_terrain = df.iloc[ + :, left_monster_end:left_field_end + ].values.astype(float) + right_monsters = df.iloc[ + :, left_field_end:right_monster_end + ].values.astype(float) + right_terrain = df.iloc[ + :, right_monster_end:right_field_end + ].values.astype(float) + # 合并怪物特征(只使用怪物部分进行相似度计算) self.past_left = left_monsters self.past_right = right_monsters - + # 保存地形特征用于显示 self.past_left_terrain = left_terrain self.past_right_terrain = right_terrain - + # 胜负标签 self.labels = df.iloc[:, total_features].values else: # 兼容旧格式:只有怪物特征 - self.past_left = df.iloc[:, 0:MONSTER_COUNT].values.astype(float) - self.past_right = df.iloc[:, MONSTER_COUNT:MONSTER_COUNT*2].values.astype(float) - self.labels = df.iloc[:, MONSTER_COUNT*2].values - + self.past_left = df.iloc[:, 0:MONSTER_COUNT].values.astype( + float + ) + self.past_right = df.iloc[ + :, MONSTER_COUNT : MONSTER_COUNT * 2 + ].values.astype(float) + self.labels = df.iloc[:, MONSTER_COUNT * 2].values + # 地形特征为空 - self.past_left_terrain = np.zeros((len(self.past_left), FIELD_FEATURE_COUNT)) - self.past_right_terrain = np.zeros((len(self.past_right), FIELD_FEATURE_COUNT)) - + self.past_left_terrain = np.zeros( + (len(self.past_left), FIELD_FEATURE_COUNT) + ) + self.past_right_terrain = np.zeros( + (len(self.past_right), FIELD_FEATURE_COUNT) + ) + except Exception as e: print(f"加载历史数据失败: {e}") # 加载失败时,初始化为空数组 @@ -76,14 +102,18 @@ def load_history_data(self): self.labels = np.array([], dtype=str) # 构造历史对局特征: 左右数量之和与差的绝对值拼接(只使用怪物特征) - self.feat_past = np.hstack([ - self.past_left + self.past_right, - np.abs(self.past_left - self.past_right) - ]) + self.feat_past = np.hstack( + [ + self.past_left + self.past_right, + np.abs(self.past_left - self.past_right), + ] + ) # 历史对局总数 self.N_history = self.past_left.shape[0] - def render_similar_matches(self, left_counts: np.ndarray, right_counts: np.ndarray): + def render_similar_matches( + self, left_counts: np.ndarray, right_counts: np.ndarray + ): """返回与当前对局最相似的历史对局索引及胜率统计""" # 将输入转为浮点型数组 cur_left = left_counts.astype(float) @@ -96,7 +126,9 @@ def render_similar_matches(self, left_counts: np.ndarray, right_counts: np.ndarr need_R_idx = np.nonzero(pres_R)[0] # 当前右侧有兵的索引 # 构造当前对局特征并计算与所有历史的余弦相似度 - feat_cur = np.hstack([cur_left + cur_right, np.abs(cur_left - cur_right)]).reshape(1, -1) + feat_cur = np.hstack( + [cur_left + cur_right, np.abs(cur_left - cur_right)] + ).reshape(1, -1) sims = cosine_similarity_manual(feat_cur, self.feat_past)[0] # 历史对局的存在布尔矩阵 @@ -105,14 +137,18 @@ def render_similar_matches(self, left_counts: np.ndarray, right_counts: np.ndarr # 计算未镜像(missA, cntA)和镜像后(missB, cntB)的缺兵及数量差距 missA = np.sum(np.logical_xor(pres_L, hist_pres_L), axis=1) + np.sum( - np.logical_xor(pres_R, hist_pres_R), axis=1) + np.logical_xor(pres_R, hist_pres_R), axis=1 + ) cntA = np.sum(np.abs(self.past_left - cur_left), axis=1) + np.sum( - np.abs(self.past_right - cur_right), axis=1) + np.abs(self.past_right - cur_right), axis=1 + ) missB = np.sum(np.logical_xor(pres_L, hist_pres_R), axis=1) + np.sum( - np.logical_xor(pres_R, hist_pres_L), axis=1) + np.logical_xor(pres_R, hist_pres_L), axis=1 + ) cntB = np.sum(np.abs(self.past_right - cur_left), axis=1) + np.sum( - np.abs(self.past_left - cur_right), axis=1) + np.abs(self.past_left - cur_right), axis=1 + ) # 根据(miss, cnt)比较,决定是否对历史数据做镜像处理 swap = (missB < missA) | ((missB == missA) & (cntB < cntA)) @@ -131,18 +167,27 @@ def render_similar_matches(self, left_counts: np.ndarray, right_counts: np.ndarr full_R = np.all(Rh[:, need_R_idx] == cur_right[need_R_idx], axis=1) # 计算需求索引处的数量差和 - diff_L = np.sum(np.abs(Lh[:, need_L_idx] - cur_left[need_L_idx]), axis=1) - diff_R = np.sum(np.abs(Rh[:, need_R_idx] - cur_right[need_R_idx]), axis=1) + diff_L = np.sum( + np.abs(Lh[:, need_L_idx] - cur_left[need_L_idx]), axis=1 + ) + diff_R = np.sum( + np.abs(Rh[:, need_R_idx] - cur_right[need_R_idx]), axis=1 + ) # 计算对手兵种在本方需求中的命中数,取最小值作为 match_other - hit_L = np.sum(hist_pres_Rh[:, need_L_idx] & pres_L[need_L_idx], axis=1) - hit_R = np.sum(hist_pres_Lh[:, need_R_idx] & pres_R[need_R_idx], axis=1) + hit_L = np.sum( + hist_pres_Rh[:, need_L_idx] & pres_L[need_L_idx], axis=1 + ) + hit_R = np.sum( + hist_pres_Lh[:, need_R_idx] & pres_R[need_R_idx], axis=1 + ) match_other = np.minimum(hit_L, hit_R) # 根据命中侧及是否完全匹配,选择对应的 qdiff_other qdiff_other = np.where( - (hit_R > 0) & (~full_R), diff_R, - np.where((hit_L > 0) & (~full_L), diff_L, 0) + (hit_R > 0) & (~full_R), + diff_R, + np.where((hit_L > 0) & (~full_L), diff_L, 0), ) # 批量计算分类所需的布尔向量 @@ -171,7 +216,9 @@ def render_similar_matches(self, left_counts: np.ndarray, right_counts: np.ndarr # 且左右两侧比例相同,且比例不为 1(确保“数量均不同”) # 注意:当某侧不存在任意单位时,认为该侧比例为 1 且恒定 if need_L_idx.size > 0: - ratios_L = Lh[:, need_L_idx] / np.maximum(cur_left[need_L_idx], 1e-12) + ratios_L = Lh[:, need_L_idx] / np.maximum( + cur_left[need_L_idx], 1e-12 + ) rL_min = ratios_L.min(axis=1) rL_max = ratios_L.max(axis=1) uniform_L = np.isclose(rL_min, rL_max, rtol=1e-3, atol=1e-6) @@ -181,7 +228,9 @@ def render_similar_matches(self, left_counts: np.ndarray, right_counts: np.ndarr rL = np.ones(self.N_history, dtype=float) if need_R_idx.size > 0: - ratios_R = Rh[:, need_R_idx] / np.maximum(cur_right[need_R_idx], 1e-12) + ratios_R = Rh[:, need_R_idx] / np.maximum( + cur_right[need_R_idx], 1e-12 + ) rR_min = ratios_R.min(axis=1) rR_max = ratios_R.max(axis=1) uniform_R = np.isclose(rR_min, rR_max, rtol=1e-3, atol=1e-6) @@ -191,7 +240,9 @@ def render_similar_matches(self, left_counts: np.ndarray, right_counts: np.ndarr rR = np.ones(self.N_history, dtype=float) same_ratio = np.isclose(rL, rR, rtol=1e-3, atol=1e-6) - ratio_not_one = ~np.isclose(rL, 1.0, rtol=1e-3, atol=1e-6) # rL==rR 时即可代表两侧都不为1 + ratio_not_one = ~np.isclose( + rL, 1.0, rtol=1e-3, atol=1e-6 + ) # rL==rR 时即可代表两侧都不为1 proportional = uniform_L & uniform_R & same_ratio & ratio_not_one # 2类:同种类,数量均不同且成比例 @@ -215,14 +266,44 @@ def render_similar_matches(self, left_counts: np.ndarray, right_counts: np.ndarr # 从前5条中计算左右胜率 top5 = top20[:5] - labs = np.where(swap[top5], np.where(self.labels[top5]=="L", "R", "L"), self.labels[top5]) - tgtL = need_L_idx[np.argmax(cur_left[need_L_idx])] if need_L_idx.size else None - tgtR = need_R_idx[np.argmax(cur_right[need_R_idx])] if need_R_idx.size else None - - lw = np.sum([lab == ("L" if (Lh[i, tgtL] if tgtL is not None else 0) > 0 else "R") - for i, lab in zip(top5, labs)]) - rw = np.sum([lab == ("L" if (Lh[i, tgtR] if tgtR is not None else 0) > 0 else "R") - for i, lab in zip(top5, labs)]) + labs = np.where( + swap[top5], + np.where(self.labels[top5] == "L", "R", "L"), + self.labels[top5], + ) + tgtL = ( + need_L_idx[np.argmax(cur_left[need_L_idx])] + if need_L_idx.size + else None + ) + tgtR = ( + need_R_idx[np.argmax(cur_right[need_R_idx])] + if need_R_idx.size + else None + ) + + lw = np.sum( + [ + lab + == ( + "L" + if (Lh[i, tgtL] if tgtL is not None else 0) > 0 + else "R" + ) + for i, lab in zip(top5, labs) + ] + ) + rw = np.sum( + [ + lab + == ( + "L" + if (Lh[i, tgtR] if tgtR is not None else 0) > 0 + else "R" + ) + for i, lab in zip(top5, labs) + ] + ) self.left_rate = lw / len(top5) if top5.size else 0 self.right_rate = rw / len(top5) if top5.size else 0 self.sims = sims @@ -235,19 +316,24 @@ def get_terrain_names(self, idx, is_swapped=False): """获取指定历史对局的地形名称""" if idx >= len(self.past_left_terrain): return "无地形" - + # 根据是否镜像选择地形特征 - terrain_features = self.past_right_terrain[idx] if is_swapped else self.past_left_terrain[idx] - + terrain_features = ( + self.past_right_terrain[idx] + if is_swapped + else self.past_left_terrain[idx] + ) + # 获取激活的地形特征索引 active_indices = np.where(terrain_features > 0)[0] - + if len(active_indices) == 0: return "无地形" - + # 尝试从FieldRecognizer获取实际的特征列名称 try: - from field_recognition import FieldRecognizer + from src.recognition.field_recognition import FieldRecognizer + field_recognizer = FieldRecognizer() if field_recognizer.is_ready(): feature_columns = field_recognizer.get_feature_columns() @@ -298,11 +384,13 @@ def get_terrain_names(self, idx, is_swapped=False): # 如果无法识别,使用原名称的简化版本 simple_name = full_name.replace("_", "") active_terrains.append(simple_name) - - return "+".join(active_terrains) if active_terrains else "无地形" + + return ( + "+".join(active_terrains) if active_terrains else "无地形" + ) except Exception: pass - + # 备用硬编码映射(如果无法获取FieldRecognizer) # 与main.py的terrain_display_mapping保持一致 terrain_names = { @@ -317,14 +405,14 @@ def get_terrain_names(self, idx, is_swapped=False): 8: "顶部弩炮", 9: "侧边弩炮", 10: "侧边火炮", - 11: "顶部火炮" + 11: "顶部火炮", } - + # 获取所有激活地形的名称 active_terrains = [] for i in active_indices: if i < len(terrain_names): active_terrains.append(terrain_names[i]) - + # 如果有多个地形,用"+"连接 return "+".join(active_terrains) if active_terrains else "无地形" diff --git a/src/analysis/winning_rate_statistics.py b/src/analysis/winning_rate_statistics.py new file mode 100644 index 0000000..1daa56c --- /dev/null +++ b/src/analysis/winning_rate_statistics.py @@ -0,0 +1,1131 @@ +import pandas as pd +from math import sqrt +from collections import defaultdict +from src.core.config import MONSTER_COUNT, FIELD_FEATURE_COUNT, MONSTER_DATA +from src.core.paths import image_path, PROJECT_ROOT + +FIELD_FEATURE_COUNT = 0 + + +def load_data(): + """加载数据""" + df = pd.read_csv( + PROJECT_ROOT / "data" / "arknights.csv", header=None, low_memory=False + ) + # 设置列名 + monster_cols_left = [f"L{i+1}" for i in range(MONSTER_COUNT)] + field_cols_left = [f"FL{i+1}" for i in range(FIELD_FEATURE_COUNT)] + monster_cols_right = [f"R{i+1}" for i in range(MONSTER_COUNT)] + field_cols_right = [f"FR{i+1}" for i in range(FIELD_FEATURE_COUNT)] + + df.columns = ( + monster_cols_left + + field_cols_left + + monster_cols_right + + field_cols_right + + ["Result", "ImgPath"] + ) + return df + + +def get_monster_name(monster_id): + """根据怪物ID获取怪物名称""" + if monster_id in MONSTER_DATA.index: + return MONSTER_DATA.loc[monster_id]["名称"] + return f"怪物{monster_id}" + + +def get_monster_original_name(monster_id): + """根据怪物ID获取怪物原始名称(用于匹配图片)""" + if monster_id in MONSTER_DATA.index: + return MONSTER_DATA.loc[monster_id]["原始名称"] + return f"怪物{monster_id}" + + +def monster_image_uri(monster_name): + path = image_path(monster_name) + if not path.exists(): + path = image_path("empty") + return path.resolve().as_uri() + + +def monster_img_tag(monster_name, size): + uri = monster_image_uri(monster_name) + empty_uri = monster_image_uri("empty") + return ( + f'' + ) + + +def calculate_all_monster_win_rates(df): + """计算所有怪物的胜率""" + monster_stats = {} + total_matches = len(df) + + for i in range(1, MONSTER_COUNT + 1): + try: + # 转换数据类型并过滤 + df[f"L{i}"] = pd.to_numeric(df[f"L{i}"], errors="coerce").fillna(0) + df[f"R{i}"] = pd.to_numeric(df[f"R{i}"], errors="coerce").fillna(0) + + # 左方统计 + left_games = df[df[f"L{i}"] != 0] + left_wins = len(left_games[left_games["Result"] == "L"]) + left_total = len(left_games) + + # 右方统计 + right_games = df[df[f"R{i}"] != 0] + right_wins = len(right_games[right_games["Result"] == "R"]) + right_total = len(right_games) + except Exception as e: + print(f"处理怪物{i}时出错: {e}") + continue + + # 合并统计 + total_games = left_total + right_total + total_wins = left_wins + right_wins + + if total_games > 0: + monster_name = get_monster_name(i) + win_rate = total_wins / total_games + participation_rate = ( + total_games / total_matches if total_matches > 0 else 0 + ) + monster_stats[monster_name] = { + "怪物ID": i, + "胜场": total_wins, + "总场数": total_games, + "胜率": win_rate, + "参战率": participation_rate, + } + + return pd.DataFrame(monster_stats).T.sort_values("胜率", ascending=False) + + +def analyze_monster_combinations(df): + """分析怪物配合效果""" + # 初始化数据结构 + single_stats = defaultdict(lambda: {"appearances": 0, "wins": 0}) + pair_stats = defaultdict(lambda: {"co_occurrences": 0, "co_wins": 0}) + + for _, record in df.iterrows(): + victory_side = record["Result"] + + # 获取左右两方的怪物 + left_monsters = [] + right_monsters = [] + + for i in range(1, MONSTER_COUNT + 1): + try: + left_val = ( + float(record[f"L{i}"]) if pd.notna(record[f"L{i}"]) else 0 + ) + right_val = ( + float(record[f"R{i}"]) if pd.notna(record[f"R{i}"]) else 0 + ) + + if left_val > 0: + left_monsters.append(i) + if right_val > 0: + right_monsters.append(i) + except (ValueError, TypeError): + continue + + # 确定胜利队伍和失败队伍 + if victory_side == "L": + win_team, lose_team = left_monsters, right_monsters + else: + win_team, lose_team = right_monsters, left_monsters + + # 更新单怪统计(胜利方) + for monster in win_team: + single_stats[monster]["appearances"] += 1 + single_stats[monster]["wins"] += 1 + + # 更新单怪统计(失败方) + for monster in lose_team: + single_stats[monster]["appearances"] += 1 + + # 更新双怪组合统计(胜利方) + for i in range(len(win_team)): + for j in range(i + 1, len(win_team)): + x, y = sorted((win_team[i], win_team[j])) + pair_stats[(x, y)]["co_occurrences"] += 1 + pair_stats[(x, y)]["co_wins"] += 1 + + # 更新双怪组合统计(失败方) + for i in range(len(lose_team)): + for j in range(i + 1, len(lose_team)): + x, y = sorted((lose_team[i], lose_team[j])) + pair_stats[(x, y)]["co_occurrences"] += 1 + + # 计算最佳配合 + results = [] + total_battles = len(df) + + for (x, y), stats in pair_stats.items(): + if stats["co_occurrences"] < 10: # 过滤低频组合 + continue + + # 组合胜率 + xy_win_rate = stats["co_wins"] / stats["co_occurrences"] + + # 单怪胜率 + if ( + single_stats[x]["appearances"] > 0 + and single_stats[y]["appearances"] > 0 + ): + x_win_rate = ( + single_stats[x]["wins"] / single_stats[x]["appearances"] + ) + y_win_rate = ( + single_stats[y]["wins"] / single_stats[y]["appearances"] + ) + + # 提升度 - 简化计算,不使用卡方检验 + expected_win_rate = sqrt(x_win_rate * y_win_rate) + lift = ( + xy_win_rate / expected_win_rate if expected_win_rate > 0 else 0 + ) + + if ( + lift > 1.1 + and xy_win_rate > max(x_win_rate, y_win_rate) + and stats["co_occurrences"] >= 20 + ): + x_name = get_monster_name(x) + y_name = get_monster_name(y) + results.append( + { + "组合": f"{x_name}+{y_name}", + "怪物1": x_name, + "怪物2": y_name, + "ID1": x, + "ID2": y, + "提升度": lift, + "组合胜率": xy_win_rate, + "出场次数": stats["co_occurrences"], + "获胜次数": stats["co_wins"], + } + ) + + # 按提升度排序 + results.sort(key=lambda x: -x["提升度"]) + return pd.DataFrame(results) + + +def find_countered_monsters(df): + """寻找被克制的怪物前五个""" + counter_stats = defaultdict(lambda: {"total_matchups": 0, "losses": 0}) + + for _, record in df.iterrows(): + victory_side = record["Result"] + + # 获取左右两方的怪物 + left_monsters = [] + right_monsters = [] + + for i in range(1, MONSTER_COUNT + 1): + try: + left_val = ( + float(record[f"L{i}"]) if pd.notna(record[f"L{i}"]) else 0 + ) + right_val = ( + float(record[f"R{i}"]) if pd.notna(record[f"R{i}"]) else 0 + ) + + if left_val > 0: + left_monsters.append(i) + if right_val > 0: + right_monsters.append(i) + except (ValueError, TypeError): + continue + + # 分析对战情况 + for left_monster in left_monsters: + for right_monster in right_monsters: + # 左方怪物的统计 + counter_stats[left_monster]["total_matchups"] += 1 + if victory_side == "R": # 左方败北 + counter_stats[left_monster]["losses"] += 1 + + # 右方怪物的统计 + counter_stats[right_monster]["total_matchups"] += 1 + if victory_side == "L": # 右方败北 + counter_stats[right_monster]["losses"] += 1 + + # 计算被克制率 + countered_results = [] + for monster_id, stats in counter_stats.items(): + if stats["total_matchups"] >= 20: # 至少20场对战 + loss_rate = stats["losses"] / stats["total_matchups"] + monster_name = get_monster_name(monster_id) + countered_results.append( + { + "怪物": monster_name, + "怪物ID": monster_id, + "被克制率": loss_rate, + "败场": stats["losses"], + "总对战数": stats["total_matchups"], + } + ) + + # 按被克制率排序(降序) + countered_results.sort(key=lambda x: -x["被克制率"]) + return pd.DataFrame(countered_results[:5]) + + +def analyze_individual_monster_relations(df): + """分析每个怪物的详细关系:最佳队友、克制关系、被克制关系""" + monster_relations = {} + + # 初始化数据结构 + single_stats = defaultdict(lambda: {"appearances": 0, "wins": 0}) + pair_stats = defaultdict(lambda: {"co_occurrences": 0, "co_wins": 0}) + counter_stats = defaultdict( + lambda: defaultdict(lambda: {"matchups": 0, "wins": 0}) + ) + + for _, record in df.iterrows(): + victory_side = record["Result"] + + # 获取左右两方的怪物 + left_monsters = [] + right_monsters = [] + + for i in range(1, MONSTER_COUNT + 1): + try: + left_val = ( + float(record[f"L{i}"]) if pd.notna(record[f"L{i}"]) else 0 + ) + right_val = ( + float(record[f"R{i}"]) if pd.notna(record[f"R{i}"]) else 0 + ) + + if left_val > 0: + left_monsters.append(i) + if right_val > 0: + right_monsters.append(i) + except (ValueError, TypeError): + continue + + # 确定胜利队伍和失败队伍 + if victory_side == "L": + win_team, lose_team = left_monsters, right_monsters + else: + win_team, lose_team = right_monsters, left_monsters + + # 更新单怪统计 + for monster in win_team: + single_stats[monster]["appearances"] += 1 + single_stats[monster]["wins"] += 1 + + for monster in lose_team: + single_stats[monster]["appearances"] += 1 + + # 更新队友统计 + for i in range(len(win_team)): + for j in range(i + 1, len(win_team)): + x, y = sorted((win_team[i], win_team[j])) + pair_stats[(x, y)]["co_occurrences"] += 1 + pair_stats[(x, y)]["co_wins"] += 1 + + for i in range(len(lose_team)): + for j in range(i + 1, len(lose_team)): + x, y = sorted((lose_team[i], lose_team[j])) + pair_stats[(x, y)]["co_occurrences"] += 1 + + # 更新克制关系统计 + for winner in win_team: + for loser in lose_team: + counter_stats[winner][loser]["matchups"] += 1 + counter_stats[winner][loser]["wins"] += 1 + counter_stats[loser][winner]["matchups"] += 1 + + # 为每个怪物分析关系 + for monster_id in range(1, MONSTER_COUNT + 1): + monster_name = get_monster_name(monster_id) + + if single_stats[monster_id]["appearances"] < 10: # 数据量太少 + continue + + # 分析最佳队友 + best_teammates = [] + for (x, y), stats in pair_stats.items(): + if x == monster_id or y == monster_id: + partner_id = y if x == monster_id else x + if stats["co_occurrences"] >= 5: # 至少5次合作 + combo_win_rate = stats["co_wins"] / stats["co_occurrences"] + + # 计算提升度 + if ( + single_stats[monster_id]["appearances"] > 0 + and single_stats[partner_id]["appearances"] > 0 + ): + monster_win_rate = ( + single_stats[monster_id]["wins"] + / single_stats[monster_id]["appearances"] + ) + partner_win_rate = ( + single_stats[partner_id]["wins"] + / single_stats[partner_id]["appearances"] + ) + expected_win_rate = sqrt( + monster_win_rate * partner_win_rate + ) + + if expected_win_rate > 0: + lift = combo_win_rate / expected_win_rate + if lift > 1.0: + best_teammates.append( + { + "partner_id": partner_id, + "partner_name": get_monster_name( + partner_id + ), + "lift": lift, + "combo_win_rate": combo_win_rate, + "occurrences": stats["co_occurrences"], + } + ) + + best_teammates.sort(key=lambda x: -x["lift"]) + + # 分析克制关系 + counters = [] # 该怪物克制的 + countered_by = [] # 克制该怪物的 + + for opponent_id in counter_stats[monster_id]: + stats = counter_stats[monster_id][opponent_id] + if stats["matchups"] >= 5: + win_rate = stats["wins"] / stats["matchups"] + if win_rate > 0.6: # 胜率超过60%认为克制 + counters.append( + { + "opponent_id": opponent_id, + "opponent_name": get_monster_name(opponent_id), + "win_rate": win_rate, + "matchups": stats["matchups"], + } + ) + + for opponent_id in range(1, MONSTER_COUNT + 1): + if ( + opponent_id in counter_stats + and monster_id in counter_stats[opponent_id] + ): + stats = counter_stats[opponent_id][monster_id] + if stats["matchups"] >= 5: + lose_rate = stats["wins"] / stats["matchups"] + if lose_rate > 0.6: # 对方胜率超过60%认为被克制 + countered_by.append( + { + "opponent_id": opponent_id, + "opponent_name": get_monster_name(opponent_id), + "lose_rate": lose_rate, + "matchups": stats["matchups"], + } + ) + + counters.sort(key=lambda x: -x["win_rate"]) + countered_by.sort(key=lambda x: -x["lose_rate"]) + + monster_relations[monster_id] = { + "name": monster_name, + "best_teammates": best_teammates[:3], + "counters": counters[:3], + "countered_by": countered_by[:3], + } + + return monster_relations + + +def get_terrain_feature_columns(): + """获取地形特征列名""" + import json + import re + from collections import defaultdict + + try: + # 加载类别映射 + class_map_path = "tools/battlefield_recognize/class_to_idx.json" + with open(class_map_path, "r", encoding="utf-8") as f: + class_to_idx = json.load(f) + + # 使用与data_cleaning_with_field_recognize_gpu.py相同的逻辑 + grouped_elements = defaultdict(list) + for class_name in class_to_idx.keys(): + if class_name.endswith("_none"): + continue + condensed_name = re.sub(r"_left_", "_", class_name) + condensed_name = re.sub(r"_right_", "_", condensed_name) + grouped_elements[condensed_name].append(class_name) + + # 返回排序后的特征列名 + return sorted(grouped_elements.keys()) + except Exception as e: + print(f"无法获取地形特征列名,使用默认值: {e}") + # 如果无法获取,返回默认列表 + return [ + "altar_vertical_altar", + "block_parallel_block", + "block_vertical_altar_shape1", + "block_vertical_altar_shape2", + "block_vertical_block_shape1", + "block_vertical_block_shape2", + "coil_narrow_coil", + "coil_wide_coil", + "crossbow_top_crossbow", + "fire_side_crossbow", + "fire_side_fire", + "fire_top_fire", + ] + + +def analyze_terrain_effects(df): + """分析地形对怪物的影响""" + terrain_effects = [] + + # 获取实际的地形特征列名 + terrain_feature_columns = get_terrain_feature_columns() + + # 地形显示名称映射 + terrain_display_mapping = { + "altar_vertical_altar": "垂直祭坛", + "block_parallel_block": "平行方块阻挡", + "block_vertical_altar_shape1": "垂直祭坛形阻挡1", + "block_vertical_altar_shape2": "垂直祭坛形阻挡2", + "block_vertical_block_shape1": "垂直方块阻挡1", + "block_vertical_block_shape2": "垂直方块阻挡2", + "coil_narrow_coil": "窄型线圈装置", + "coil_wide_coil": "宽型线圈装置", + "crossbow_top_crossbow": "顶部弩炮", + "fire_side_crossbow": "侧边弩炮", + "fire_side_fire": "侧边火炮", + "fire_top_fire": "顶部火炮", + } + + for terrain_idx, terrain_key in enumerate(terrain_feature_columns): + terrain_name = terrain_display_mapping.get(terrain_key, terrain_key) + + for monster_idx in range(1, MONSTER_COUNT + 1): + monster_name = get_monster_name(monster_idx) + + # 转换数据类型 + df[f"FL{terrain_idx+1}"] = pd.to_numeric( + df[f"FL{terrain_idx+1}"], errors="coerce" + ).fillna(0) + df[f"FR{terrain_idx+1}"] = pd.to_numeric( + df[f"FR{terrain_idx+1}"], errors="coerce" + ).fillna(0) + df[f"L{monster_idx}"] = pd.to_numeric( + df[f"L{monster_idx}"], errors="coerce" + ).fillna(0) + df[f"R{monster_idx}"] = pd.to_numeric( + df[f"R{monster_idx}"], errors="coerce" + ).fillna(0) + + # 有地形时的表现 + terrain_left_games = df[ + (df[f"FL{terrain_idx+1}"] == 1) & (df[f"L{monster_idx}"] > 0) + ] + terrain_right_games = df[ + (df[f"FR{terrain_idx+1}"] == 1) & (df[f"R{monster_idx}"] > 0) + ] + + terrain_total = len(terrain_left_games) + len(terrain_right_games) + if terrain_total < 5: # 数据量太少 + continue + + terrain_wins = len( + terrain_left_games[terrain_left_games["Result"] == "L"] + ) + len(terrain_right_games[terrain_right_games["Result"] == "R"]) + terrain_win_rate = terrain_wins / terrain_total + + # 无地形时的表现 + normal_left_games = df[ + (df[f"FL{terrain_idx+1}"] == 0) & (df[f"L{monster_idx}"] > 0) + ] + normal_right_games = df[ + (df[f"FR{terrain_idx+1}"] == 0) & (df[f"R{monster_idx}"] > 0) + ] + + normal_total = len(normal_left_games) + len(normal_right_games) + if normal_total < 5: + continue + + normal_wins = len( + normal_left_games[normal_left_games["Result"] == "L"] + ) + len(normal_right_games[normal_right_games["Result"] == "R"]) + normal_win_rate = normal_wins / normal_total + + # 计算影响程度 + effect = terrain_win_rate - normal_win_rate + + if abs(effect) >= 0.05: # 胜率差异超过5%才记录 + terrain_effects.append( + { + "地形": terrain_name, + "怪物": monster_name, + "怪物ID": monster_idx, + "地形胜率": terrain_win_rate, + "普通胜率": normal_win_rate, + "影响程度": effect, + "地形场次": terrain_total, + "普通场次": normal_total, + } + ) + + # 按影响程度绝对值排序 + terrain_effects.sort(key=lambda x: -abs(x["影响程度"])) + return pd.DataFrame(terrain_effects[:20]) # 增加到前20个 + + +def analyze_device_counter_effects(df): + """分析五个装置对怪物的克制效果""" + device_counter_results = {} + + # 获取实际的地形特征列名 + terrain_feature_columns = get_terrain_feature_columns() + + # 定义五个装置类别及其对应的地形特征 + device_categories = { + "altar": { + "name": "祭坛", + "features": [f for f in terrain_feature_columns if "altar" in f], + "description": "祭坛类装置", + }, + "block": { + "name": "箱子/阻挡", + "features": [f for f in terrain_feature_columns if "block" in f], + "description": "方块阻挡类装置", + }, + "coil": { + "name": "电桩", + "features": [f for f in terrain_feature_columns if "coil" in f], + "description": "线圈电桩装置", + }, + "crossbow": { + "name": "弩箭", + "features": [ + f for f in terrain_feature_columns if "crossbow" in f + ], + "description": "弩炮装置", + }, + "fire": { + "name": "火炮", + "features": [f for f in terrain_feature_columns if "fire" in f], + "description": "火炮装置", + }, + } + + for device_key, device_info in device_categories.items(): + device_name = device_info["name"] + device_features = device_info["features"] + + if not device_features: + continue + + device_effects = [] + + # 对每个怪物分析该装置的克制效果 + for monster_idx in range(1, MONSTER_COUNT + 1): + monster_name = get_monster_name(monster_idx) + + # 收集该装置所有特征的统计数据 + total_device_games = 0 + total_device_wins = 0 + total_normal_games = 0 + total_normal_wins = 0 + + for terrain_key in device_features: + if terrain_key not in terrain_feature_columns: + continue + + terrain_idx = terrain_feature_columns.index(terrain_key) + + # 转换数据类型 + df[f"FL{terrain_idx+1}"] = pd.to_numeric( + df[f"FL{terrain_idx+1}"], errors="coerce" + ).fillna(0) + df[f"FR{terrain_idx+1}"] = pd.to_numeric( + df[f"FR{terrain_idx+1}"], errors="coerce" + ).fillna(0) + df[f"L{monster_idx}"] = pd.to_numeric( + df[f"L{monster_idx}"], errors="coerce" + ).fillna(0) + df[f"R{monster_idx}"] = pd.to_numeric( + df[f"R{monster_idx}"], errors="coerce" + ).fillna(0) + + # 有该装置时的表现 + device_left_games = df[ + (df[f"FL{terrain_idx+1}"] == 1) + & (df[f"L{monster_idx}"] > 0) + ] + device_right_games = df[ + (df[f"FR{terrain_idx+1}"] == 1) + & (df[f"R{monster_idx}"] > 0) + ] + + device_games_count = len(device_left_games) + len( + device_right_games + ) + device_wins_count = len( + device_left_games[device_left_games["Result"] == "L"] + ) + len( + device_right_games[device_right_games["Result"] == "R"] + ) + + # 无该装置时的表现 + normal_left_games = df[ + (df[f"FL{terrain_idx+1}"] == 0) + & (df[f"L{monster_idx}"] > 0) + ] + normal_right_games = df[ + (df[f"FR{terrain_idx+1}"] == 0) + & (df[f"R{monster_idx}"] > 0) + ] + + normal_games_count = len(normal_left_games) + len( + normal_right_games + ) + normal_wins_count = len( + normal_left_games[normal_left_games["Result"] == "L"] + ) + len( + normal_right_games[normal_right_games["Result"] == "R"] + ) + + total_device_games += device_games_count + total_device_wins += device_wins_count + total_normal_games += normal_games_count + total_normal_wins += normal_wins_count + + # 计算整体效果 + if ( + total_device_games >= 10 and total_normal_games >= 10 + ): # 确保有足够的数据 + device_win_rate = total_device_wins / total_device_games + normal_win_rate = total_normal_wins / total_normal_games + effect = device_win_rate - normal_win_rate + + # 计算克制程度(负值表示被该装置克制) + counter_effect = -effect # 装置对怪物的克制效果 + + if abs(effect) >= 0.05: # 胜率差异超过5%才记录 + device_effects.append( + { + "怪物": monster_name, + "怪物ID": monster_idx, + "装置胜率": device_win_rate, + "普通胜率": normal_win_rate, + "克制程度": counter_effect, # 正值表示被该装置克制 + "装置场次": total_device_games, + "普通场次": total_normal_games, + "效果类型": ( + "被克制" if counter_effect > 0 else "克制装置" + ), + } + ) + + # 按克制程度排序(被克制程度最高的在前) + device_effects.sort(key=lambda x: -x["克制程度"]) + device_counter_results[device_key] = { + "name": device_name, + "description": device_info["description"], + "features": device_features, + "effects": device_effects[:10], # 取前10个被克制最严重的怪物 + } + + return device_counter_results + + +def create_html_table( + df, columns, title, is_combo=False, monster_relations=None +): + """创建带有怪物头像的HTML表格""" + html = f"

{title}

\n\n" + + # 表头 + if is_combo: + html += "" + else: + html += "" + + for col in columns: + html += f"" + + # 如果是胜率表且有关系数据,添加额外的列 + if not is_combo and monster_relations and title == "所有怪物胜率排行榜": + html += "" + + html += "\n" + + # 表格内容 + row_number = 1 + for idx, row in df.iterrows(): + html += "" + + # 怪物图片和名称 + monster_id = None + if is_combo and "ID1" in row and "ID2" in row: + monster1_name = get_monster_name(row["ID1"]) + monster2_name = get_monster_name(row["ID2"]) + monster1_orig = get_monster_original_name(row["ID1"]) + monster2_orig = get_monster_original_name(row["ID2"]) + html += f"""""" + elif "怪物ID" in row: + monster_name = get_monster_name(row["怪物ID"]) + monster_orig = get_monster_original_name(row["怪物ID"]) + display_name = row.get("怪物", monster_name) + monster_id = row["怪物ID"] + html += f"""""" + else: + # 对于胜率表,使用索引作为怪物名称 + monster_name = idx + # 尝试从怪物数据中获取ID + monster_orig = monster_name + for mid in range(1, MONSTER_COUNT + 1): + if get_monster_name(mid) == monster_name: + monster_id = mid + monster_orig = get_monster_original_name(mid) + break + html += f"""""" + + # 数据列 + for col in columns: + value = row[col] + if isinstance(value, float): + if "率" in col or "程度" in col: + html += f"" + else: + html += f"" + else: + html += f"" + + # 如果是胜率表且有关系数据,添加关系信息 + if ( + not is_combo + and monster_relations + and title == "所有怪物胜率排行榜" + and monster_id + ): + relations = monster_relations.get(int(monster_id), {}) + + # 最佳队友 + html += "" + + # 克制关系 + html += "" + + # 被克制关系 + html += "" + + html += "\n" + row_number += 1 + + html += "
组合怪物{col}最佳队友克制被克制
+ {row_number}. + {monster_img_tag(monster1_orig, 30)} + {monster1_name}
+ {monster_img_tag(monster2_orig, 30)} + {monster2_name} +
+ {row_number}. + {monster_img_tag(monster_orig, 50)} + {display_name} + + {row_number}. + {monster_img_tag(monster_orig, 50)} + {monster_name} + {value:.2%}{value:.2f}{value}" + if ( + relations + and "best_teammates" in relations + and relations["best_teammates"] + ): + teammates = [] + for teammate in relations["best_teammates"]: + teammate_orig = get_monster_original_name( + teammate["partner_id"] + ) + teammates.append(f"""
+ {monster_img_tag(teammate_orig, 20)} + {teammate['partner_name']} ({teammate['lift']:.2f}x) +
""") + html += "".join(teammates) + else: + html += "暂无数据" + html += "
" + if relations and "counters" in relations and relations["counters"]: + counters = [] + for counter in relations["counters"]: + counter_orig = get_monster_original_name( + counter["opponent_id"] + ) + counters.append(f"""
+ {monster_img_tag(counter_orig, 20)} + {counter['opponent_name']} ({counter['win_rate']:.0%}) +
""") + html += "".join(counters) + else: + html += "暂无数据" + html += "
" + if ( + relations + and "countered_by" in relations + and relations["countered_by"] + ): + countered = [] + for counter in relations["countered_by"]: + counter_orig = get_monster_original_name( + counter["opponent_id"] + ) + countered.append(f"""
+ {monster_img_tag(counter_orig, 20)} + {counter['opponent_name']} ({counter['lose_rate']:.0%}) +
""") + html += "".join(countered) + else: + html += "暂无数据" + html += "
\n" + return html + + +def create_device_counter_html(device_counter_effects): + """创建装置克制效果的HTML表格""" + html = "

装置克制效果统计

\n" + + for device_key, device_data in device_counter_effects.items(): + if not device_data["effects"]: + continue + + device_name = device_data["name"] + device_description = device_data["description"] + effects = device_data["effects"] + + html += f"

{device_name}({device_description})

\n" + html += "\n" + html += "" + html += "\n" + + row_number = 1 + for effect in effects: + monster_name = effect["怪物"] + monster_orig = get_monster_original_name(effect["怪物ID"]) + html += f""" + + + + + + + + \n""" + row_number += 1 + + html += "
怪物克制程度装置胜率普通胜率装置场次普通场次效果类型
+ {row_number}. + {monster_img_tag(monster_orig, 40)} + {monster_name} + {effect['克制程度']:.2%}{effect['装置胜率']:.2%}{effect['普通胜率']:.2%}{effect['装置场次']}{effect['普通场次']}{effect['效果类型']}
\n
\n" + + return html + + +def generate_comprehensive_report(): + """生成综合统计报告""" + print("正在加载数据...") + df = load_data() + + print("正在计算怪物胜率...") + win_rates = calculate_all_monster_win_rates(df) + + print("正在分析怪物配合...") + combinations = analyze_monster_combinations(df) + + print("正在分析被克制关系...") + countered = find_countered_monsters(df) + + if FIELD_FEATURE_COUNT > 0: + print("正在分析地形效果...") + terrain_effects = analyze_terrain_effects(df) + + print("正在分析装置克制效果...") + device_counter_effects = analyze_device_counter_effects(df) + else: + print("地形特征数量为0,跳过地形分析...") + terrain_effects = pd.DataFrame() + device_counter_effects = {} + + print("正在分析个体怪物关系...") + monster_relations = analyze_individual_monster_relations(df) + + # 创建HTML报告 + total_battles = len(df) + monster_count = MONSTER_COUNT + field_count = FIELD_FEATURE_COUNT + + html = f""" + + + + + + +

明日方舟争锋频道绿藤城

+
+

数据概览:

+
    +
  • 总战斗记录:{total_battles} 场
  • +
  • 统计怪物数量:{monster_count} 种
  • +
  • 地形特征数量:{field_count} 种
  • +
+
+""" + + # 1. 所有怪物胜率 + if not win_rates.empty: + html += create_html_table( + win_rates, + ["胜场", "总场数", "胜率", "参战率"], + "所有怪物胜率排行榜", + monster_relations=monster_relations, + ) + else: + html += "

所有怪物胜率排行榜

暂无数据

" + + # 2. 最佳配合 + if not combinations.empty: + html += create_html_table( + combinations.head(20), + ["提升度", "组合胜率", "出场次数"], + "最佳怪物配合TOP20", + is_combo=True, + ) + else: + html += "

最佳怪物配合

暂无足够的配合数据

" + + # 4. 地形效果 + if not terrain_effects.empty: + html += create_html_table( + terrain_effects, + ["地形", "地形胜率", "普通胜率", "影响程度"], + "地形影响最大的怪物TOP20", + ) + else: + html += "

地形影响

暂无足够的地形数据

" + + # 5. 装置克制效果 + if device_counter_effects: + html += create_device_counter_html(device_counter_effects) + else: + html += "

装置克制效果统计

暂无足够的装置数据

" + + timestamp = pd.Timestamp.now().strftime("%Y-%m-%d %H:%M:%S") + html += f""" +
+

报告生成时间:{timestamp}

+

注:数据基于历史战斗记录,仅供参考

+
+ + +""" + + # 保存报告 + with open("comprehensive_monster_report.html", "w", encoding="utf-8") as f: + f.write(html) + + print("统计报告已生成:comprehensive_monster_report.html") + + # 也输出到控制台 + print("\n=== 怪物胜率TOP10 ===") + print(win_rates.head(10).to_string()) + + if not combinations.empty: + print("\n=== 最佳配合TOP10 ===") + print( + combinations.head(10)[ + ["组合", "提升度", "组合胜率", "出场次数"] + ].to_string() + ) + + # 输出装置克制效果 + if device_counter_effects: + print("\n=== 装置克制效果统计 ===") + for device_key, device_data in device_counter_effects.items(): + if device_data["effects"]: + print( + f"\n{device_data['name']}({device_data['description']})最克制的怪物TOP5:" + ) + for i, effect in enumerate(device_data["effects"][:5]): + print( + f" {i+1}. {effect['怪物']} - 克制程度: {effect['克制程度']:.2%} ({effect['效果类型']})" + ) + + if not terrain_effects.empty: + print("\n=== 地形影响TOP10 ===") + print( + terrain_effects.head(10)[ + ["地形", "怪物", "影响程度", "地形胜率", "普通胜率"] + ].to_string() + ) + + +if __name__ == "__main__": + generate_comprehensive_report() diff --git "a/data_train/package/\346\225\260\346\215\256\345\214\205\346\224\276\345\234\250\346\255\244\350\267\257\345\276\204.md" b/src/core/__init__.py similarity index 100% rename from "data_train/package/\346\225\260\346\215\256\345\214\205\346\224\276\345\234\250\346\255\244\350\267\257\345\276\204.md" rename to src/core/__init__.py diff --git a/config.py b/src/core/config.py similarity index 54% rename from config.py rename to src/core/config.py index 31acae5..819cea0 100644 --- a/config.py +++ b/src/core/config.py @@ -1,13 +1,14 @@ -from pathlib import Path import cv2 import numpy as np import pandas as pd import logging +from .paths import DATA_DIR, IMAGES_DIR + logger = logging.getLogger(__name__) -# 全局地形特征数量常量 -FIELD_FEATURE_COUNT = 0 # 默认值 +FIELD_FEATURE_COUNT = 0 + def load_images() -> dict[str, np.ndarray]: """ @@ -15,13 +16,13 @@ def load_images() -> dict[str, np.ndarray]: returns: dict - 图片字典,键为文件名(不含扩展名),值为numpy.ndarray对象 """ images = {} - images_path = Path('images') - # 遍历images目录下的所有文件 - for image_file in images_path.glob('*.*'): - if image_file.suffix.lower() in ('.png', '.jpg', '.jpeg', '.bmp'): + images_path = IMAGES_DIR + for image_file in images_path.glob("*.*"): + if image_file.suffix.lower() in (".png", ".jpg", ".jpeg", ".bmp"): try: - # img = cv2.imread(str(image_file), cv2.IMREAD_COLOR) - img = cv2.imdecode(np.fromfile(image_file, dtype=np.uint8), cv2.IMREAD_COLOR) + img = cv2.imdecode( + np.fromfile(image_file, dtype=np.uint8), cv2.IMREAD_COLOR + ) if img is None: logger.error(f"无法加载图片: {image_file}") continue @@ -30,13 +31,19 @@ def load_images() -> dict[str, np.ndarray]: logger.error(f"加载图片出错 {image_file}: {str(e)}") return images + MONSTER_IMAGES = load_images() + def load_monster_data(): - monster_data = pd.read_csv('monster_greenvine.csv', index_col="id", encoding='utf-8-sig') + monster_data = pd.read_csv( + DATA_DIR / "monster_greenvine.csv", + index_col="id", + encoding="utf-8-sig", + ) return monster_data + MONSTER_DATA = load_monster_data() -# 全局变量 -MONSTER_COUNT = len(MONSTER_DATA) # 根据怪物数据自动设置数量 +MONSTER_COUNT = len(MONSTER_DATA) diff --git a/constants.py b/src/core/constants.py similarity index 89% rename from constants.py rename to src/core/constants.py index 60d3adc..a1a2590 100644 --- a/constants.py +++ b/src/core/constants.py @@ -7,10 +7,10 @@ "health": 1390, "magic_resist": 0, "attack_interval": 3.3, - "move_speed": 1 / 2, # 除2是因为方舟就是要除2 + "move_speed": 1 / 2, # 除2是因为方舟就是要除2 "attack_radius": 2.75, "effect": "破甲15", - "icon": "images/1.png" + "icon": "", }, 2: { "name": "灼热源石虫", @@ -23,7 +23,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "images/2.png" + "icon": "", }, 3: { "name": "狂暴的猎狗pro", @@ -36,7 +36,7 @@ "move_speed": 1.9 / 2, "attack_radius": 0.8, "effect": "", - "icon": "images/3.png" + "icon": "", }, 4: { "name": "炮击组长", @@ -49,7 +49,7 @@ "move_speed": 0.8 / 2, "attack_radius": 7, "effect": "溅射", - "icon": "images/4.png" + "icon": "", }, 5: { "name": "萨卡兹大剑手", @@ -62,7 +62,7 @@ "move_speed": 0.85 / 2, "attack_radius": 0.8, "effect": "", - "icon": "images/5.png" + "icon": "", }, 6: { "name": "宿主流浪者", @@ -75,7 +75,7 @@ "move_speed": 0.65 / 2, "attack_radius": 0.8, "effect": "再生", - "icon": "images/6.png" + "icon": "", }, 7: { "name": "重装防御者", @@ -88,7 +88,7 @@ "move_speed": 0.75 / 2, "attack_radius": 0.8, "effect": "", - "icon": "images/7.png" + "icon": "", }, 8: { "name": "复仇者", @@ -101,7 +101,7 @@ "move_speed": 0.65 / 2, "attack_radius": 0.8, "effect": "火刀", - "icon": "images/8.png" + "icon": "", }, 9: { "name": "狂暴宿主组长", @@ -114,7 +114,7 @@ "move_speed": 1.2 / 2, "attack_radius": 0.8, "effect": "掉血", - "icon": "images/9.png" + "icon": "", }, 10: { "name": "巧克力流心虫虫", @@ -127,7 +127,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "images/2.png" + "icon": "", }, 11: { "name": "巧克力流心虫虫", @@ -140,7 +140,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "images/2.png" + "icon": "", }, 12: { "name": "泥岩巨像", @@ -153,7 +153,7 @@ "move_speed": 0.4 / 2, "attack_radius": 0.8, "effect": "", - "icon": "images/12.png" + "icon": "", }, 13: { "name": "高塔术师", @@ -166,7 +166,7 @@ "move_speed": 0.6 / 2, "attack_radius": 3.2, "effect": "打2 溅射", - "icon": "images/13.png" + "icon": "", }, 14: { "name": "巧克力流心虫虫", @@ -179,7 +179,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "images/2.png" + "icon": "", }, 15: { "name": "巧克力流心虫虫", @@ -192,7 +192,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "images/2.png" + "icon": "", }, 16: { "name": "冰原术师", @@ -205,7 +205,7 @@ "move_speed": 0.7 / 2, "attack_radius": 3.2, "effect": "打2 寒冷", - "icon": "images/16.png" + "icon": "", }, 17: { "name": "巧克力流心虫虫", @@ -218,7 +218,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "images/2.png" + "icon": "", }, 18: { "name": "弧光锋卫长", @@ -231,7 +231,7 @@ "move_speed": 0.75 / 2, "attack_radius": 0.8, "effect": "", - "icon": "images/18.png" + "icon": "", }, 19: { "name": "巧克力流心虫虫", @@ -244,7 +244,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "images/2.png" + "icon": "", }, 20: { "name": "提亚卡乌好战者", @@ -257,7 +257,7 @@ "move_speed": 0.8 / 2, "attack_radius": 0.8, "effect": "破甲10", - "icon": "images/20.png" + "icon": "", }, 21: { "name": "矿脉守卫", @@ -270,7 +270,7 @@ "move_speed": 0.6 / 2, "attack_radius": 0.8, "effect": "反伤", - "icon": "images/21.png" + "icon": "", }, 22: { "name": "巧克力流心虫虫", @@ -283,7 +283,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "images/2.png" + "icon": "", }, 23: { "name": "巧克力流心虫虫", @@ -296,7 +296,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "images/2.png" + "icon": "", }, 24: { "name": "巧克力流心虫虫", @@ -309,7 +309,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "images/2.png" + "icon": "", }, 25: { "name": "巧克力流心虫虫", @@ -322,7 +322,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "images/2.png" + "icon": "", }, 26: { "name": "巧克力流心虫虫", @@ -335,6 +335,6 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "images/2.png" - } -} \ No newline at end of file + "icon": "", + }, +} diff --git a/src/core/paths.py b/src/core/paths.py new file mode 100644 index 0000000..4c9dbec --- /dev/null +++ b/src/core/paths.py @@ -0,0 +1,38 @@ +from pathlib import Path + + +PROJECT_ROOT = Path(__file__).resolve().parents[2] +SRC_DIR = PROJECT_ROOT / "src" +RESOURCES_DIR = SRC_DIR / "resources" +ASSETS_DIR = RESOURCES_DIR / "assets" +IMAGES_DIR = ASSETS_DIR / "images" +PROCESS_IMAGES_DIR = IMAGES_DIR / "process" +TMP_IMAGES_DIR = IMAGES_DIR / "tmp" +DATA_DIR = RESOURCES_DIR / "data" +SIMULATION_DIR = SRC_DIR / "simulation" +TOOLS_DIR = SRC_DIR / "tools" + + +def resource_path(*parts: str) -> Path: + return RESOURCES_DIR.joinpath(*parts) + + +def image_path(name: str) -> Path: + return IMAGES_DIR / f"{name}.png" + + +def process_image_path(name: str | int) -> Path: + return PROCESS_IMAGES_DIR / f"{name}.png" + + +def data_path(name: str) -> Path: + return DATA_DIR / name + + +def simulation_path(name: str) -> Path: + return SIMULATION_DIR / name + + +def ensure_tmp_images_dir() -> Path: + TMP_IMAGES_DIR.mkdir(parents=True, exist_ok=True) + return TMP_IMAGES_DIR diff --git a/src/data/__init__.py b/src/data/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/data_package.py b/src/data/data_package.py similarity index 96% rename from data_package.py rename to src/data/data_package.py index dbc90b5..79fe590 100644 --- a/data_package.py +++ b/src/data/data_package.py @@ -36,10 +36,11 @@ def create_zip_package(output_zip_path): shutil.rmtree(folder) print(f"已删除文件夹:{folder}") except Exception as e: - print(f"删除文件夹 {folder} 时出错:{e}") + print(f"删除文件夹 {folder} 时出错:{e}") print(f"压缩包已创建:{output_zip_path}") + def package_data(): # 使用当前时间生成输出文件名 current_time = datetime.now().strftime("%Y_%m_%d__%H_%M_%S") @@ -49,5 +50,6 @@ def package_data(): create_zip_package(output_zip) return output_zip + if __name__ == "__main__": package_data() diff --git a/loadData.py b/src/data/load_data.py similarity index 76% rename from loadData.py rename to src/data/load_data.py index 5481459..4c6f6d6 100644 --- a/loadData.py +++ b/src/data/load_data.py @@ -5,21 +5,18 @@ import logging import gzip import win32gui -import win32api import win32con -import sys import os from pathlib import Path -from winrt_capture import WinRTScreenCapture +from src.game.winrt_capture import WinRTScreenCapture logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) - class AdbConnector: def __init__(self, adb_serial=None): - self.adb_path = r".\platform-tools\adb.exe" + self.adb_path = r".\vendor\bin\platform-tools\adb.exe" self.screen_width = 0 self.screen_height = 0 self.device_serial = adb_serial if adb_serial else "" @@ -29,7 +26,9 @@ def connect(self): # 初始化设备序列号 try: # 如果已经有序列号,则尝试更新该序列号;否则使用默认值 - target_serial = self.device_serial if self.device_serial else "127.0.0.1:5555" + target_serial = ( + self.device_serial if self.device_serial else "127.0.0.1:5555" + ) self.update_device_serial(target_serial) logger.info(f"最终使用设备: {self.device_serial}") except RuntimeError as e: @@ -63,7 +62,11 @@ def get_window_size(self): # 执行ADB命令获取分辨率 size_cmd = f"{self.adb_path} -s {self.device_serial} shell wm size" result = subprocess.run( - size_cmd, shell=True, capture_output=True, text=True, check=True + size_cmd, + shell=True, + capture_output=True, + text=True, + check=True, ) output = result.stdout.strip() @@ -84,9 +87,13 @@ def get_window_size(self): else: screen_width = height screen_height = width - logger.info(f"成功获取模拟器分辨率: {screen_width}x{screen_height}") + logger.info( + f"成功获取模拟器分辨率: {screen_width}x{screen_height}" + ) except Exception as e: # 否则使用默认分辨率 - logger.exception(f"获取分辨率失败,使用默认分辨率1920x1080。错误: {e}") + logger.exception( + f"获取分辨率失败,使用默认分辨率1920x1080。错误: {e}" + ) screen_width = 1920 screen_height = 1080 return screen_width, screen_height @@ -95,7 +102,11 @@ def get_device_list(self): try: device_cmd = f"{self.adb_path} devices" result = subprocess.run( - device_cmd, shell=True, capture_output=True, text=True, timeout=5 + device_cmd, + shell=True, + capture_output=True, + text=True, + timeout=5, ) devices: list[str] = [] for line in result.stdout.split("\n"): @@ -118,7 +129,11 @@ def update_device_serial(self, serial): # 检查手动设备是否在线 device_cmd = f"{self.adb_path} devices" result = subprocess.run( - device_cmd, shell=True, capture_output=True, text=True, timeout=5 + device_cmd, + shell=True, + capture_output=True, + text=True, + timeout=5, ) # 只在调试模式下输出完整设备列表 logger.debug(f"ADB devices输出:\n{result.stdout}") @@ -134,7 +149,9 @@ def update_device_serial(self, serial): return dev # 只使用指定的设备,不要自动选择其他设备 - logger.error(f"未找到指定的设备: {serial},当前在线设备: {devices}") + logger.error( + f"未找到指定的设备: {serial},当前在线设备: {devices}" + ) self.device_serial = "" return "" @@ -188,10 +205,14 @@ def decode_raw(self, data: bytes): # 确保数据长度正确(实际屏幕分辨率,4通道) expected_length = self.screen_width * self.screen_height * 4 if len(argb_array) != expected_length: - raise ValueError(f"Invalid data length for {self.screen_width}x{self.screen_height} ARGB image") + raise ValueError( + f"Invalid data length for {self.screen_width}x{self.screen_height} ARGB image" + ) # 转换为正确的形状 (高度, 宽度, 通道) - argb_array = argb_array.reshape((self.screen_height, self.screen_width, 4)) + argb_array = argb_array.reshape( + (self.screen_height, self.screen_width, 4) + ) # 分离Alpha通道(如果需要保留Alpha,可以去掉这步) # 这里将ARGB转换为BGR(OpenCV默认格式) @@ -208,17 +229,19 @@ def decode_raw_with_gzip(self, data: bytes): image = self.decode_raw(decompressed_data) return image except Exception as e: - logger.exception("Gzip decompression or image decoding failed: %s", e) + logger.exception( + "Gzip decompression or image decoding failed: %s", e + ) return None def capture_screenshot_raw_gzip(self): - get_raw_gzip_cmd = ( - rf'{self.adb_path} -s {self.device_serial} exec-out "screencap | gzip -1"' - ) + get_raw_gzip_cmd = rf'{self.adb_path} -s {self.device_serial} exec-out "screencap | gzip -1"' ta = time.time() try: # 获取经过gzip压缩的二进制图像数据 - screenshot_raw_gzip = subprocess.check_output(get_raw_gzip_cmd, shell=True) + screenshot_raw_gzip = subprocess.check_output( + get_raw_gzip_cmd, shell=True + ) image = self.decode_raw_with_gzip(screenshot_raw_gzip) if image is None: raise RuntimeError("OpenCV failed to decode image") @@ -260,39 +283,51 @@ def connect(self): rect = win32gui.GetClientRect(self.hwnd) self.screen_width = rect[2] - rect[0] self.screen_height = rect[3] - rect[1] - + try: - from maa_adb_connector import resolve_maafw_path + from ..game.maa_adb_connector import resolve_maafw_path + binary_path = resolve_maafw_path() if binary_path: os.environ["MAAFW_BINARY_PATH"] = binary_path - + from maa.toolkit import Toolkit - from maa.controller import Win32Controller, MaaWin32ScreencapMethodEnum, MaaWin32InputMethodEnum + from maa.controller import ( + Win32Controller, + MaaWin32ScreencapMethodEnum, + MaaWin32InputMethodEnum, + ) + Toolkit.init_option(str(Path.cwd())) - + # 既然纯 SendMessage 被引擎无视,我们退一步使用 SendMessageWithCursorPos # 它会在瞬间把鼠标光标移动到目标位置发送消息,再瞬间移回原位。这种方式可能不会强制将游戏窗口调回前台。 self.maa_ctrl = Win32Controller( self.hwnd, screencap_method=MaaWin32ScreencapMethodEnum.FramePool, mouse_method=MaaWin32InputMethodEnum.SendMessageWithCursorPos, - keyboard_method=MaaWin32InputMethodEnum.SendMessageWithCursorPos + keyboard_method=MaaWin32InputMethodEnum.SendMessageWithCursorPos, ) self.maa_ctrl.post_connection().wait() - + # 设置截图使用原始大小,防止因为MAA默认缩放导致外部坐标及图像切割计算出错 self.maa_ctrl.set_screenshot_use_raw_size(True) - - logger.info(f"已成功通过 MaaFramework 接管 PC 窗口 (支持后台操作)") + + logger.info( + f"已成功通过 MaaFramework 接管 PC 窗口 (支持后台操作)" + ) except Exception as e: - logger.warning(f"MaaFramework 初始化失败,退回原有前台实现: {e}") + logger.warning( + f"MaaFramework 初始化失败,退回原有前台实现: {e}" + ) self.maa_ctrl = None self.capture = WinRTScreenCapture(window_name=self.window_name) self.capture.start() self.is_connected = True - logger.info(f"成功连接到PC端窗口: {self.window_name}, 分辨率: {self.screen_width}x{self.screen_height}") + logger.info( + f"成功连接到PC端窗口: {self.window_name}, 分辨率: {self.screen_width}x{self.screen_height}" + ) else: logger.warning(f"未找到PC端窗口: {self.window_name}") self.is_connected = False @@ -300,7 +335,7 @@ def connect(self): def capture_screenshot(self): if not self.is_connected: return None - + if self.maa_ctrl: try: self.maa_ctrl.post_screencap().wait() @@ -317,7 +352,7 @@ def capture_screenshot(self): def click(self, point): if not self.hwnd: return - + try: # PC端点击需要重新获取一次ClientRect以防窗口大小改变 rect = win32gui.GetClientRect(self.hwnd) @@ -327,17 +362,21 @@ def click(self, point): x, y = point x_coord = int(x * self.screen_width) y_coord = int(y * self.screen_height) - + if self.maa_ctrl: logger.info(f"MAA后台点击坐标: ({x_coord}, {y_coord})") self.maa_ctrl.post_click(x_coord, y_coord).wait() return - client_left, client_top = win32gui.ClientToScreen(self.hwnd, (0, 0)) + client_left, client_top = win32gui.ClientToScreen( + self.hwnd, (0, 0) + ) screen_x = client_left + x_coord screen_y = client_top + y_coord - - logger.info(f"PC端点击坐标: 窗口内({x_coord}, {y_coord}) -> 屏幕({screen_x}, {screen_y})") + + logger.info( + f"PC端点击坐标: 窗口内({x_coord}, {y_coord}) -> 屏幕({screen_x}, {screen_y})" + ) # 尝试将窗口置于前台,忽略可能的错误 try: @@ -351,16 +390,42 @@ def click(self, point): # 使用底层 SendInput 模拟鼠标事件,支持多显示器 (VIRTUALDESK) 并且不会在权限不足时抛出异常 import ctypes - + PUL = ctypes.POINTER(ctypes.c_ulong) + class KeyBdInput(ctypes.Structure): - _fields_ = [("wVk", ctypes.c_ushort), ("wScan", ctypes.c_ushort), ("dwFlags", ctypes.c_ulong), ("time", ctypes.c_ulong), ("dwExtraInfo", PUL)] + _fields_ = [ + ("wVk", ctypes.c_ushort), + ("wScan", ctypes.c_ushort), + ("dwFlags", ctypes.c_ulong), + ("time", ctypes.c_ulong), + ("dwExtraInfo", PUL), + ] + class HardwareInput(ctypes.Structure): - _fields_ = [("uMsg", ctypes.c_ulong), ("wParamL", ctypes.c_short), ("wParamH", ctypes.c_ushort)] + _fields_ = [ + ("uMsg", ctypes.c_ulong), + ("wParamL", ctypes.c_short), + ("wParamH", ctypes.c_ushort), + ] + class MouseInput(ctypes.Structure): - _fields_ = [("dx", ctypes.c_long), ("dy", ctypes.c_long), ("mouseData", ctypes.c_ulong), ("dwFlags", ctypes.c_ulong), ("time", ctypes.c_ulong), ("dwExtraInfo", PUL)] + _fields_ = [ + ("dx", ctypes.c_long), + ("dy", ctypes.c_long), + ("mouseData", ctypes.c_ulong), + ("dwFlags", ctypes.c_ulong), + ("time", ctypes.c_ulong), + ("dwExtraInfo", PUL), + ] + class Input_I(ctypes.Union): - _fields_ = [("ki", KeyBdInput), ("mi", MouseInput), ("hi", HardwareInput)] + _fields_ = [ + ("ki", KeyBdInput), + ("mi", MouseInput), + ("hi", HardwareInput), + ] + class Input(ctypes.Structure): _fields_ = [("type", ctypes.c_ulong), ("ii", Input_I)] @@ -368,11 +433,19 @@ class Input(ctypes.Structure): SM_YVIRTUALSCREEN = 77 SM_CXVIRTUALSCREEN = 78 SM_CYVIRTUALSCREEN = 79 - - vscreen_x = ctypes.windll.user32.GetSystemMetrics(SM_XVIRTUALSCREEN) - vscreen_y = ctypes.windll.user32.GetSystemMetrics(SM_YVIRTUALSCREEN) - vscreen_w = ctypes.windll.user32.GetSystemMetrics(SM_CXVIRTUALSCREEN) - vscreen_h = ctypes.windll.user32.GetSystemMetrics(SM_CYVIRTUALSCREEN) + + vscreen_x = ctypes.windll.user32.GetSystemMetrics( + SM_XVIRTUALSCREEN + ) + vscreen_y = ctypes.windll.user32.GetSystemMetrics( + SM_YVIRTUALSCREEN + ) + vscreen_w = ctypes.windll.user32.GetSystemMetrics( + SM_CXVIRTUALSCREEN + ) + vscreen_h = ctypes.windll.user32.GetSystemMetrics( + SM_CYVIRTUALSCREEN + ) if vscreen_w == 0 or vscreen_h == 0: vscreen_w = 1920 @@ -390,25 +463,58 @@ class Input(ctypes.Structure): extra = ctypes.c_ulong(0) ii_ = Input_I() - + # 移动鼠标 - ii_.mi = MouseInput(dx, dy, 0, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK, 0, ctypes.pointer(extra)) + ii_.mi = MouseInput( + dx, + dy, + 0, + MOUSEEVENTF_MOVE + | MOUSEEVENTF_ABSOLUTE + | MOUSEEVENTF_VIRTUALDESK, + 0, + ctypes.pointer(extra), + ) cmd = Input(ctypes.c_ulong(0), ii_) - ctypes.windll.user32.SendInput(1, ctypes.pointer(cmd), ctypes.sizeof(cmd)) - + ctypes.windll.user32.SendInput( + 1, ctypes.pointer(cmd), ctypes.sizeof(cmd) + ) + time.sleep(0.05) - + # 按下左键 - ii_.mi = MouseInput(dx, dy, 0, MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK, 0, ctypes.pointer(extra)) + ii_.mi = MouseInput( + dx, + dy, + 0, + MOUSEEVENTF_LEFTDOWN + | MOUSEEVENTF_ABSOLUTE + | MOUSEEVENTF_VIRTUALDESK, + 0, + ctypes.pointer(extra), + ) cmd = Input(ctypes.c_ulong(0), ii_) - ctypes.windll.user32.SendInput(1, ctypes.pointer(cmd), ctypes.sizeof(cmd)) - + ctypes.windll.user32.SendInput( + 1, ctypes.pointer(cmd), ctypes.sizeof(cmd) + ) + time.sleep(0.05) - + # 抬起左键 - ii_.mi = MouseInput(dx, dy, 0, MOUSEEVENTF_LEFTUP | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK, 0, ctypes.pointer(extra)) + ii_.mi = MouseInput( + dx, + dy, + 0, + MOUSEEVENTF_LEFTUP + | MOUSEEVENTF_ABSOLUTE + | MOUSEEVENTF_VIRTUALDESK, + 0, + ctypes.pointer(extra), + ) cmd = Input(ctypes.c_ulong(0), ii_) - ctypes.windll.user32.SendInput(1, ctypes.pointer(cmd), ctypes.sizeof(cmd)) + ctypes.windll.user32.SendInput( + 1, ctypes.pointer(cmd), ctypes.sizeof(cmd) + ) except Exception as e: logger.exception(f"PC端点击出错: {e}") diff --git a/src/game/__init__.py b/src/game/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/auto_fetch.py b/src/game/auto_fetch.py similarity index 74% rename from auto_fetch.py rename to src/game/auto_fetch.py index a17cf02..9dc6f04 100644 --- a/auto_fetch.py +++ b/src/game/auto_fetch.py @@ -1,6 +1,7 @@ import os + # 设置 OpenCV 日志级别为 ERROR,减少 libpng 警告 -os.environ['OPENCV_LOG_LEVEL'] = 'ERROR' +os.environ["OPENCV_LOG_LEVEL"] = "ERROR" import csv import datetime @@ -9,19 +10,19 @@ from pathlib import Path import threading import time -from typing import Literal import cv2 import numpy as np -import loadData -from recognize import intelligent_workers_debug -from config import MONSTER_COUNT, FIELD_FEATURE_COUNT +from src.recognition.recognize import INTELLIGENT_WORKERS_DEBUG +from src.core.config import MONSTER_COUNT, FIELD_FEATURE_COUNT +from src.core.paths import PROJECT_ROOT, process_image_path, simulation_path from collections.abc import Callable from collections import deque -from login import LoginManager +from .login import LoginManager logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) + class GameState(Enum): MAIN_MENU = auto() MODE_SELECTION_UNSELECTED = auto() @@ -32,6 +33,7 @@ class GameState(Enum): FINISHED = auto() UNKNOWN = auto() + class AutoFetch: def __init__( self, @@ -47,7 +49,7 @@ def __init__( recognizer=None, cannot_model=None, field_recognizer=None, - start_timestamp=None, + start_timestamp=None, ): self.connector = connector self.game_mode = game_mode # 游戏模式(30人或自娱自乐) @@ -66,10 +68,14 @@ def __init__( self.monster_image = None # 当前轮次怪物图片 self.auto_fetch_running = False # 自动获取数据的状态 self.auto_fetch_thread = None # 线程引用 - self.start_time = start_timestamp if start_timestamp is not None else time.time() # 使用预先确定的时间戳 + self.start_time = ( + start_timestamp if start_timestamp is not None else time.time() + ) # 使用预先确定的时间戳 self.training_duration = training_duration # 训练时长 - self.data_folder = Path(f"data") # 数据文件夹路径 - self.image_buffer = deque(maxlen=5) # 图片缓存队列,设置队列长短来保存结算前的图片 + self.data_folder = PROJECT_ROOT / "data" # 数据文件夹路径 + self.image_buffer = deque( + maxlen=5 + ) # 图片缓存队列,设置队列长短来保存结算前的图片 self.recognizer = recognizer # 使用传入的识别器 self.cannot_model = cannot_model # 使用传入的模型 self.last_state = GameState.UNKNOWN @@ -79,22 +85,24 @@ def __init__( # 初始化状态匹配模板,缩小匹配尺寸提高速度 self.MATCH_WIDTH = 1920 // 4 self.MATCH_HEIGHT = 1080 // 4 // 4 - + # 初始化模板 self.processed_template = [] self._init_templates() - + # 根据 FIELD_FEATURE_COUNT 决定是否启用场地识别器(使用传入的实例) if FIELD_FEATURE_COUNT > 0: if self.field_recognizer is not None: logger.info(f"场地识别已启用,特征数量: {FIELD_FEATURE_COUNT}") else: - logger.warning(f"FIELD_FEATURE_COUNT={FIELD_FEATURE_COUNT} > 0 但未传入 field_recognizer,场地识别将被禁用") + logger.warning( + f"FIELD_FEATURE_COUNT={FIELD_FEATURE_COUNT} > 0 但未传入 field_recognizer,场地识别将被禁用" + ) self.field_recognizer = None else: self.field_recognizer = None logger.info("场地识别已禁用,仅收集怪物数据") - + def _log(self, level, message): """生成带有设备序列号的日志消息""" serial = getattr(self.connector, "device_serial", None) @@ -105,10 +113,14 @@ def _log(self, level, message): def _init_templates(self): for i in range(16): - img = cv2.imread(f"images/process/{i}.png") + img = cv2.imread(str(process_image_path(i))) if img is not None: # 使用最近邻插值缩放模板,速度最快 - img_resized = cv2.resize(img, (self.MATCH_WIDTH, self.MATCH_HEIGHT * 4), interpolation=cv2.INTER_NEAREST) + img_resized = cv2.resize( + img, + (self.MATCH_WIDTH, self.MATCH_HEIGHT * 4), + interpolation=cv2.INTER_NEAREST, + ) img_quarter = img_resized[self.MATCH_HEIGHT * 3 :, :] self.processed_template.append(img_quarter) else: @@ -119,18 +131,31 @@ def match_images(self, screenshot): # 裁剪底部 1/4 ROI y_start = int(h * 3 / 4) screenshot_quarter = screenshot[y_start:, :] - screenshot_quarter = cv2.resize(screenshot_quarter, (self.MATCH_WIDTH, self.MATCH_HEIGHT), interpolation=cv2.INTER_NEAREST) - + screenshot_quarter = cv2.resize( + screenshot_quarter, + (self.MATCH_WIDTH, self.MATCH_HEIGHT), + interpolation=cv2.INTER_NEAREST, + ) + results = [] for idx, template in enumerate(self.processed_template): if template is None: continue - res = cv2.matchTemplate(screenshot_quarter, template, cv2.TM_CCOEFF_NORMED) + res = cv2.matchTemplate( + screenshot_quarter, template, cv2.TM_CCOEFF_NORMED + ) _, max_val, _, _ = cv2.minMaxLoc(res) results.append((idx, max_val)) return results - def fill_data(self, battle_result, recognize_results, monster_image, result_image, field_recognize_result): + def fill_data( + self, + battle_result, + recognize_results, + monster_image, + result_image, + field_recognize_result, + ): # 获取队列头的图片 if self.image_buffer: _, previous_image, _ = self.image_buffer[0] # 获取队列头的图片 @@ -142,7 +167,9 @@ def fill_data(self, battle_result, recognize_results, monster_image, result_imag logger.error("未找到2秒前的图片,无法保存") return - image_name = self.get_image_name(recognize_results, battle_result) # 生成图片名称 + image_name = self.get_image_name( + recognize_results, battle_result + ) # 生成图片名称 # 确保images文件夹存在 images_folder = self.data_folder / "images" @@ -152,12 +179,20 @@ def fill_data(self, battle_result, recognize_results, monster_image, result_imag except Exception as e: logger.error(f"创建images文件夹失败: {e}") - if intelligent_workers_debug: # 如果处于debug模式,保存人工审核图片到本地 + if ( + INTELLIGENT_WORKERS_DEBUG + ): # 如果处于debug模式,保存人工审核图片到本地 if monster_image is not None: try: - resized_monster_img = cv2.resize(monster_image, (960, 540)) # 调整分辨率为 960x540 + resized_monster_img = cv2.resize( + monster_image, (960, 540) + ) # 调整分辨率为 960x540 image_path = images_folder / (image_name + ".jpg") - cv2.imwrite(image_path, resized_monster_img, [int(cv2.IMWRITE_JPEG_QUALITY), 80]) + cv2.imwrite( + image_path, + resized_monster_img, + [int(cv2.IMWRITE_JPEG_QUALITY), 80], + ) # logger.info(f"保存怪物图片到 {image_path}") except Exception as e: logger.error(f"保存怪物图片失败: {e}") @@ -167,15 +202,17 @@ def fill_data(self, battle_result, recognize_results, monster_image, result_imag try: result_image_name = image_name + "_result.jpg" # 缩放到128像素高度 - (h, w) = result_image.shape[:2] + h, w = result_image.shape[:2] new_height = 128 - resized_image = cv2.resize(result_image, (int(w * (new_height / h)), new_height)) + resized_image = cv2.resize( + result_image, (int(w * (new_height / h)), new_height) + ) image_path = images_folder / result_image_name cv2.imwrite(image_path, resized_image) logger.info(f"保存结果图片到 {image_path}") except Exception as e: logger.error(f"保存结果图片失败: {e}") - + # 原始怪物数据 left_monster_data = np.zeros(MONSTER_COUNT) right_monster_data = np.zeros(MONSTER_COUNT) @@ -201,8 +238,8 @@ def fill_data(self, battle_result, recognize_results, monster_image, result_imag field_feature_columns = self.field_recognizer.get_feature_columns() field_data_values = [] for col in field_feature_columns: - if col in field_recoginze_result: - field_data_values.append(field_recoginze_result[col]) + if col in field_recognize_result: + field_data_values.append(field_recognize_result[col]) else: field_data_values.append(0) # 默认值 @@ -223,9 +260,9 @@ def fill_data(self, battle_result, recognize_results, monster_image, result_imag logger.info("仅收集怪物数据,跳过场地特征") data_row.extend(left_monster_data.tolist()) # 左侧怪物数据 data_row.extend(right_monster_data.tolist()) # 右侧怪物数据 - + data_row.append(battle_result) # Result - + # 替换所有NaN为-1 for i, x in enumerate(data_row): if isinstance(x, (int, float)) and np.isnan(x): @@ -236,7 +273,9 @@ def fill_data(self, battle_result, recognize_results, monster_image, result_imag r"%Y_%m_%d__%H_%M_%S" ) - if intelligent_workers_debug: # 如果处于debug模式,保存人工审核图片到本地 + if ( + INTELLIGENT_WORKERS_DEBUG + ): # 如果处于debug模式,保存人工审核图片到本地 data_row.append(image_name) with open(self.data_folder / "arknights.csv", "a", newline="") as file: @@ -249,24 +288,26 @@ def build_terrain_features(self, left_counts, right_counts): # 获取场地特征列数 field_feature_columns = self.field_recognizer.get_feature_columns() num_field_features = len(field_feature_columns) - + # 构建地形特征向量(基于当前场地识别结果) terrain_features = np.zeros(num_field_features) - + if self.field_recognize_result: # 将场地识别结果转换为特征向量 for i, col in enumerate(field_feature_columns): if col in self.field_recognize_result: terrain_features[i] = self.field_recognize_result[col] - + # 按照data_cleaning_with_field_recognize.py的格式组织数据 - full_features = np.concatenate([ - left_counts, # 1L-77L - terrain_features, # 78L-83L - right_counts, # 1R-77R - terrain_features # 78R-83R - ]) - + full_features = np.concatenate( + [ + left_counts, # 1L-77L + terrain_features, # 78L-83L + right_counts, # 1R-77R + terrain_features, # 78R-83R + ] + ) + return full_features @staticmethod @@ -277,7 +318,9 @@ def get_saturation(bgr): cmax = max(r, g, b) cmin = min(r, g, b) delta = cmax - cmin - return (delta / cmax) * 255 if cmax != 0 else 0 # 返回0-255范围的饱和度值 + return ( + (delta / cmax) * 255 if cmax != 0 else 0 + ) # 返回0-255范围的饱和度值 if image is None: logger.error("图像加载失败") @@ -292,14 +335,14 @@ def get_saturation(bgr): (0.9, 0.1), # 右上区域 (0.9, 0.5), # 右中区域 ] - + sample_size = 20 # 增大采样区域 left_saturations = [] right_saturations = [] - + for y_ratio, x_ratio in sample_points: y_offset = int(height * y_ratio) - + # 左侧采样点 x_left_offset = int(width * 0.1) y_end = min(y_offset + sample_size, height) @@ -308,26 +351,28 @@ def get_saturation(bgr): left_region = image[y_offset:y_end, x_left_offset:x_left_end] left_mean = left_region.mean(axis=(0, 1)) left_saturations.append(get_saturation(left_mean)) - + # 右侧采样点 x_right_offset = int(width * 0.9 - sample_size) x_right_end = min(x_right_offset + sample_size, width) if y_end > y_offset and x_right_end > x_right_offset: - right_region = image[y_offset:y_end, x_right_offset:x_right_end] + right_region = image[ + y_offset:y_end, x_right_offset:x_right_end + ] right_mean = right_region.mean(axis=(0, 1)) right_saturations.append(get_saturation(right_mean)) - + if not left_saturations or not right_saturations: logger.error("无法获取有效的采样区域") return None - + # 计算平均饱和度 avg_sat_left = sum(left_saturations) / len(left_saturations) avg_sat_right = sum(right_saturations) / len(right_saturations) - + # 计算饱和度差值 saturation_diff = avg_sat_left - avg_sat_right - + # 使用自适应阈值,根据整体饱和度水平调整 base_threshold = 15 # 如果整体饱和度较低,降低阈值 @@ -338,49 +383,51 @@ def get_saturation(bgr): threshold = 12 else: threshold = base_threshold - + # 检查差值是否符合要求 if abs(saturation_diff) <= threshold: - logger.warning(f"饱和度差值不足 (左:{avg_sat_left:.1f} vs 右:{avg_sat_right:.1f}, 阈值:{threshold})") + logger.warning( + f"饱和度差值不足 (左:{avg_sat_left:.1f} vs 右:{avg_sat_right:.1f}, 阈值:{threshold})" + ) # 尝试使用亮度差异作为备选方案 return AutoFetch.calculate_brightness_diff(image) # 返回左侧是否比右侧饱和度更高 return saturation_diff > 0 - + @staticmethod def calculate_brightness_diff(image): """使用亮度差异作为胜负识别的备选方案""" if image is None: return None - + height, width, _ = image.shape - + # 转换为灰度图 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) - + # 定义左侧和右侧区域 - left_region = gray[:, :width//2] - right_region = gray[:, width//2:] - + left_region = gray[:, : width // 2] + right_region = gray[:, width // 2 :] + # 计算平均亮度 left_brightness = left_region.mean() right_brightness = right_region.mean() - + # 计算亮度差值 brightness_diff = left_brightness - right_brightness - + # 使用亮度阈值 brightness_threshold = 10 if abs(brightness_diff) <= brightness_threshold: - logger.warning(f"亮度差值不足 (左:{left_brightness:.1f} vs 右:{right_brightness:.1f})") + logger.warning( + f"亮度差值不足 (左:{left_brightness:.1f} vs 右:{right_brightness:.1f})" + ) return None - + # 返回左侧是否比右侧亮 return brightness_diff > 0 - - @staticmethod def get_image_name(recognize_results, battle_result=None): # 处理结果 @@ -410,23 +457,37 @@ def save_statistics_to_log(self): with open("log.txt", "a", encoding="utf-8") as log_file: log_file.write(stats_text) - def recognize_and_predict(self, screenshot = None): + def recognize_and_predict(self, screenshot=None): if screenshot is None: screenshot = self.connector.capture_screenshot() self.recognize_results = self.recognizer.process_regions(screenshot) - + # 场地识别 if self.field_recognizer is not None: - self.field_recognize_result = self.field_recognizer.recognize_field_elements(screenshot) - + self.field_recognize_result = ( + self.field_recognizer.recognize_field_elements(screenshot) + ) + # 输出场地识别结果日志 if self.field_recognize_result: - detected_elements = [key for key, value in self.field_recognize_result.items() if value == 1] - partial_detected = [key for key, value in self.field_recognize_result.items() if value == -1] + detected_elements = [ + key + for key, value in self.field_recognize_result.items() + if value == 1 + ] + partial_detected = [ + key + for key, value in self.field_recognize_result.items() + if value == -1 + ] if detected_elements: - logger.info(f"场地识别检测到元素: {', '.join(detected_elements)}") + logger.info( + f"场地识别检测到元素: {', '.join(detected_elements)}" + ) if partial_detected: - logger.info(f"场地识别部分检测到元素: {', '.join(partial_detected)}") + logger.info( + f"场地识别部分检测到元素: {', '.join(partial_detected)}" + ) if not detected_elements and not partial_detected: logger.info("场地识别: 未检测到任何特殊元素") else: @@ -435,57 +496,73 @@ def recognize_and_predict(self, screenshot = None): # 场地识别被禁用,设置为空结果 self.field_recognize_result = {} logger.debug("场地识别已禁用,跳过场地识别") - + # 获取预测结果 self.update_monster_callback(self.recognize_results) left_counts = np.zeros(MONSTER_COUNT, dtype=np.int16) right_counts = np.zeros(MONSTER_COUNT, dtype=np.int16) for res in self.recognize_results: - if 'error' not in res: - region_id = res['region_id'] - matched_id = res['matched_id'] - number = res['number'] + if "error" not in res: + region_id = res["region_id"] + matched_id = res["matched_id"] + number = res["number"] if matched_id == 0: continue if region_id < 3: - left_counts[matched_id -1] = number + left_counts[matched_id - 1] = number else: - right_counts[matched_id -1] = number + right_counts[matched_id - 1] = number else: logger.error("识别结果有错误,本轮跳过") # 选择预测方法 if self.cannot_model.is_model_loaded: if self.field_recognizer is not None: # 构建包含地形的完整特征向量 - full_features = self.build_terrain_features(left_counts, right_counts) - self.current_prediction = self.cannot_model.get_prediction_with_terrain(full_features) + full_features = self.build_terrain_features( + left_counts, right_counts + ) + self.current_prediction = ( + self.cannot_model.get_prediction_with_terrain( + full_features + ) + ) else: # 仅使用怪物数据进行预测 - self.current_prediction = self.cannot_model.get_prediction(left_counts, right_counts) + self.current_prediction = self.cannot_model.get_prediction( + left_counts, right_counts + ) self.update_prediction_callback(self.current_prediction) else: logger.warning("模型未加载,无法进行预测") # 人工审核保存测试用截图 - if intelligent_workers_debug: # 如果处于debug模式且处于自动模式 - self.monster_image=screenshot + if INTELLIGENT_WORKERS_DEBUG: # 如果处于debug模式且处于自动模式 + self.monster_image = screenshot def battle_result(self, result_image): result = self.calculate_average_yellow(result_image) if result is None: logger.warning("战斗结果识别失败,需要重试") return False - + if result: self.fill_data( - "L", self.recognize_results, self.monster_image, result_image, self.field_recognize_result + "L", + self.recognize_results, + self.monster_image, + result_image, + self.field_recognize_result, ) if self.current_prediction > 0.5: self.incorrect_fill_count += 1 self._log(logging.INFO, "填写数据左赢") else: self.fill_data( - "R", self.recognize_results, self.monster_image, result_image, self.field_recognize_result + "R", + self.recognize_results, + self.monster_image, + result_image, + self.field_recognize_result, ) if self.current_prediction < 0.5: self.incorrect_fill_count += 1 @@ -495,7 +572,6 @@ def battle_result(self, result_image): self._log(logging.INFO, "下一轮") return True - def auto_fetch_data(self): relative_points = [ (0.9297, 0.8833), # 右ALL、返回主页、加入赛事、开始游戏 @@ -508,12 +584,14 @@ def auto_fetch_data(self): screenshot = self.connector.capture_screenshot() if screenshot is None: self._log(logging.ERROR, "截图失败,尝试自动登录") - + # 使用 LoginManager 的自动登录(带重启重试) - if not self.login_manager.auto_login_with_restart(lambda: self.auto_fetch_running): + if not self.login_manager.auto_login_with_restart( + lambda: self.auto_fetch_running + ): self._log(logging.ERROR, "自动登录失败,无法继续操作") return - + # 检查是否已经收到停止信号 if not self.auto_fetch_running: self._log(logging.INFO, "检测到停止信号,取消后续操作") @@ -524,7 +602,9 @@ def auto_fetch_data(self): self._log(logging.INFO, "重新获取截图") screenshot = self.connector.capture_screenshot() if screenshot is None: - self._log(logging.ERROR, "登录后仍然无法获取截图,无法继续操作") + self._log( + logging.ERROR, "登录后仍然无法获取截图,无法继续操作" + ) return # 保存当前截图及其信息到缓冲区 @@ -558,7 +638,9 @@ def auto_fetch_data(self): elif best_idx in [12, 13]: current_state = GameState.FINISHED if self.last_state != current_state: - logger.info(f"匹配到状态: {self.last_state.name} -> {current_state.name}, score:{best_score:.4f}") + logger.info( + f"匹配到状态: {self.last_state.name} -> {current_state.name}, score:{best_score:.4f}" + ) else: # logger.info(f"状态机匹配置信度过低: idx:{best_idx}, score:{best_score:.4f}") pass @@ -568,38 +650,68 @@ def auto_fetch_data(self): old_state = self.last_state self.last_state = current_state elapsed = time.time() - self.state_start_time - + # 不记录 PRE_BATTLE -> IN_BATTLE 的状态变化 - if not (old_state == GameState.PRE_BATTLE and current_state == GameState.IN_BATTLE): - self._log(logging.INFO, f"游戏状态变化: {old_state.name} -> {current_state.name}, 持续时间: {elapsed:.2f} 秒") - + if not ( + old_state == GameState.PRE_BATTLE + and current_state == GameState.IN_BATTLE + ): + self._log( + logging.INFO, + f"游戏状态变化: {old_state.name} -> {current_state.name}, 持续时间: {elapsed:.2f} 秒", + ) + # 如果成功进入稳定状态且重启计数器非零,重置重启计数器 - _stable_states = {GameState.MAIN_MENU, GameState.IN_BATTLE, GameState.SETTLEMENT, GameState.FINISHED} - if current_state in _stable_states and self.login_manager.restart_count > 0: + _stable_states = { + GameState.MAIN_MENU, + GameState.IN_BATTLE, + GameState.SETTLEMENT, + GameState.FINISHED, + } + if ( + current_state in _stable_states + and self.login_manager.restart_count > 0 + ): self.login_manager.reset_restart_count() - + self.state_start_time = time.time() # 重置状态开始时间 - + # 全局超时检测:无论什么状态,只要超过时间都可能触发重启 # 非战斗状态:超过 50 秒触发重启 # 战斗流程状态:超过 120 秒触发重启(防止战斗卡死) elapsed_time = time.time() - self.state_start_time - is_battle_state = self.last_state in [GameState.PRE_BATTLE, GameState.IN_BATTLE, GameState.SETTLEMENT, GameState.FINISHED] + is_battle_state = self.last_state in [ + GameState.PRE_BATTLE, + GameState.IN_BATTLE, + GameState.SETTLEMENT, + GameState.FINISHED, + ] timeout_threshold = 120.0 if is_battle_state else 50.0 - + # 检查是否超时 if elapsed_time > timeout_threshold: state_name = self.last_state.name if self.last_state else "NONE" - self._log(logging.WARNING, f"在状态 '{state_name}' 停留超过 {elapsed_time:.2f} 秒(阈值: {timeout_threshold:.0f} 秒),触发重启") - + self._log( + logging.WARNING, + f"在状态 '{state_name}' 停留超过 {elapsed_time:.2f} 秒(阈值: {timeout_threshold:.0f} 秒),触发重启", + ) + # 使用 LoginManager 的重启登录方法 if not self.login_manager.can_restart(): - self._log(logging.ERROR, f"已达到最大重启次数 {self.login_manager.max_restart_count} 次,停止运行") + self._log( + logging.ERROR, + f"已达到最大重启次数 {self.login_manager.max_restart_count} 次,停止运行", + ) self.auto_fetch_running = False self.stop_callback() - elif not self.login_manager.restart_and_login(first_start=False, stop_callback=lambda: self.auto_fetch_running): - self._log(logging.WARNING, "本次重启登录失败,将在下次超时后重试") - + elif not self.login_manager.restart_and_login( + first_start=False, + stop_callback=lambda: self.auto_fetch_running, + ): + self._log( + logging.WARNING, "本次重启登录失败,将在下次超时后重试" + ) + # 检测完毕后,无论结果如何,重置计时器,避免频繁阻塞 self.state_start_time = time.time() self._log(logging.INFO, "重置状态计时器") @@ -657,7 +769,9 @@ def auto_fetch_data(self): if not self._sleep_with_check(3): return if self.game_mode == "30人": - self._log(logging.INFO, "30人模式下,投资后需要等待20秒") + self._log( + logging.INFO, "30人模式下,投资后需要等待20秒" + ) if not self._sleep_with_check(5): return else: # 不投资 @@ -672,7 +786,9 @@ def auto_fetch_data(self): case GameState.SETTLEMENT: if not self.battle_result(screenshot): new_screenshot = self.connector.capture_screenshot() - if new_screenshot is not None and not self.battle_result(new_screenshot): + if new_screenshot is not None and not self.battle_result( + new_screenshot + ): self._log(logging.ERROR, "战斗结果识别失败,跳过本轮") if not self._sleep_with_check(5): return @@ -690,26 +806,33 @@ def auto_fetch_loop(self): # 每次循环开始时检查状态 if not self.auto_fetch_running: break - - self.updater() # 多开器如果不更新会强制停止 + + # 刷新当前预测显示(心跳):不要写入固定0,避免把GUI错误覆盖成“左方100%” + self.update_prediction_callback(self.current_prediction) + self.auto_fetch_data() # 每次循环结束时检查状态 if not self.auto_fetch_running: break - + elapsed_time = time.time() - self.start_time - if self.training_duration != -1 and elapsed_time >= self.training_duration: + if ( + self.training_duration != -1 + and elapsed_time >= self.training_duration + ): self._log(logging.INFO, "已达到设定时长,结束自动获取") break # 检测一次间隔时间—————————————————————————————————— - time.sleep(0.2) + time.sleep(0.1) except Exception as e: self._log(logging.ERROR, f"自动获取数据出错:\n{e}") break else: - self._log(logging.INFO, "auto_fetch_running is False, exiting loop") + self._log( + logging.INFO, "auto_fetch_running is False, exiting loop" + ) return # 不通过按钮结束自动获取 self._log(logging.INFO, "break auto_fetch_loop") @@ -719,33 +842,57 @@ def start_auto_fetch(self): if not self.auto_fetch_running: self.auto_fetch_running = True # 使用初始化时设置的时间戳,不重新获取当前时间 - start_time = datetime.datetime.fromtimestamp(self.start_time).strftime( - r"%Y_%m_%d__%H_%M_%S" - ) - self.data_folder = Path(f"data/{start_time}") + start_time = datetime.datetime.fromtimestamp( + self.start_time + ).strftime(r"%Y_%m_%d__%H_%M_%S") + self.data_folder = PROJECT_ROOT / "data" / start_time self._log(logging.INFO, f"创建文件夹: {self.data_folder}") self.data_folder.mkdir(parents=True, exist_ok=True) # 创建文件夹 (self.data_folder / "images").mkdir(parents=True, exist_ok=True) - with open(self.data_folder / "arknights.csv", "w", newline="") as file: + with open( + self.data_folder / "arknights.csv", "w", newline="" + ) as file: # 创建CSV表头 if self.field_recognizer is not None: # 获取场地特征列数 - num_field_features = len(self.field_recognizer.get_feature_columns()) - + num_field_features = len( + self.field_recognizer.get_feature_columns() + ) + # 按照data_cleaning_with_field_recognize_gpu.py的格式创建表头 - header = [f"{i+1}L" for i in range(MONSTER_COUNT)] # 1L-77L - header += [f"{i+1}LF" for i in range(MONSTER_COUNT, MONSTER_COUNT + num_field_features)] # 78LF-83LF (场地特征) - header += [f"{i+1}R" for i in range(MONSTER_COUNT)] # 1R-77R - header += [f"{i+1}RF" for i in range(MONSTER_COUNT, MONSTER_COUNT + num_field_features)] # 78RF-83RF (场地特征) + header = [ + f"{i+1}L" for i in range(MONSTER_COUNT) + ] # 1L-77L + header += [ + f"{i+1}LF" + for i in range( + MONSTER_COUNT, MONSTER_COUNT + num_field_features + ) + ] # 78LF-83LF (场地特征) + header += [ + f"{i+1}R" for i in range(MONSTER_COUNT) + ] # 1R-77R + header += [ + f"{i+1}RF" + for i in range( + MONSTER_COUNT, MONSTER_COUNT + num_field_features + ) + ] # 78RF-83RF (场地特征) header += ["Result", "ImgPath"] - logger.info(f"创建包含场地特征的CSV表头,场地特征数: {num_field_features}") + logger.info( + f"创建包含场地特征的CSV表头,场地特征数: {num_field_features}" + ) else: # 仅怪物数据的格式 - header = [f"{i+1}L" for i in range(MONSTER_COUNT)] # 左侧怪物数据 - header += [f"{i+1}R" for i in range(MONSTER_COUNT)] # 右侧怪物数据 + header = [ + f"{i+1}L" for i in range(MONSTER_COUNT) + ] # 左侧怪物数据 + header += [ + f"{i+1}R" for i in range(MONSTER_COUNT) + ] # 右侧怪物数据 header += ["Result", "ImgPath"] logger.info("创建仅包含怪物数据的CSV表头") - + writer = csv.writer(file) writer.writerow(header) self.log_file_handler = logging.FileHandler( @@ -757,9 +904,11 @@ def start_auto_fetch(self): self.log_file_handler.setFormatter(file_formatter) self.log_file_handler.setLevel(logging.INFO) logger.addHandler(self.log_file_handler) - + # 启动自动获取数据线程 - self.auto_fetch_thread = threading.Thread(target=self.auto_fetch_loop) + self.auto_fetch_thread = threading.Thread( + target=self.auto_fetch_loop + ) self.auto_fetch_thread.start() logger.info("自动获取数据已启动") self.start_callback() @@ -774,14 +923,14 @@ def _sleep_with_check(self, seconds): return False time.sleep(0.1) return True - + def stop_auto_fetch(self): if not self.auto_fetch_running: return # 强制设置停止标志,不等待线程退出 self.auto_fetch_running = False self._log(logging.INFO, "强制停止自动获取") - + # 不等待线程退出,让线程在下一次循环时自己检测到停止标志 self.save_statistics_to_log() self.stop_callback() diff --git a/login.py b/src/game/login.py similarity index 72% rename from login.py rename to src/game/login.py index fea67ae..6128b8d 100644 --- a/login.py +++ b/src/game/login.py @@ -1,6 +1,7 @@ import os + # 设置 OpenCV 日志级别为 ERROR,减少 libpng 警告 -os.environ['OPENCV_LOG_LEVEL'] = 'ERROR' +os.environ["OPENCV_LOG_LEVEL"] = "ERROR" import logging import time @@ -9,15 +10,18 @@ import cv2 import numpy as np +from src.core.paths import PROCESS_IMAGES_DIR, process_image_path + logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) + class LoginManager: """登录管理器,处理游戏登录和页面跳转""" - + def __init__(self, connector, max_restart_count=3): self.connector = connector - self.template_dir = Path("images") / "login" + self.template_dir = PROCESS_IMAGES_DIR / "login" self.template_dir.mkdir(parents=True, exist_ok=True) self.restart_count = 0 self.max_restart_count = max_restart_count @@ -26,7 +30,7 @@ def __init__(self, connector, max_restart_count=3): except Exception as e: logger.error(f"模板加载失败: {e}") self.templates = {} - + def _log(self, level, message): """生成带有设备序列号的日志消息""" serial = getattr(self.connector, "device_serial", None) @@ -34,16 +38,16 @@ def _log(self, level, message): logger.log(level, f"[{serial}] {message}") else: logger.log(level, message) - + def reset_restart_count(self): """重置重启计数器""" self.restart_count = 0 self._log(logging.INFO, "重启计数器已重置") - + def can_restart(self): """检查是否可以重启""" return self.restart_count < self.max_restart_count - + def _load_templates(self): """加载模板图片""" self.templates = {} @@ -54,38 +58,51 @@ def _load_templates(self): if template is not None: self.templates[template_name] = template logger.info(f"加载模板: {template_name}") - + def match_template(self, screenshot, template_name, threshold=0.9): """匹配模板""" if template_name not in self.templates: logger.error(f"模板不存在: {template_name}") return False, (0, 0) - + template = self.templates[template_name] - + # 转换为灰度图像 screenshot_gray = cv2.cvtColor(screenshot, cv2.COLOR_BGR2GRAY) template_gray = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY) - + # 多尺度模板匹配 found = None for scale in np.linspace(0.5, 1.5, 10): # 调整模板大小 - resized = cv2.resize(template_gray, (int(template_gray.shape[1] * scale), int(template_gray.shape[0] * scale))) - if resized.shape[0] > screenshot_gray.shape[0] or resized.shape[1] > screenshot_gray.shape[1]: + resized = cv2.resize( + template_gray, + ( + int(template_gray.shape[1] * scale), + int(template_gray.shape[0] * scale), + ), + ) + if ( + resized.shape[0] > screenshot_gray.shape[0] + or resized.shape[1] > screenshot_gray.shape[1] + ): break - + # 匹配 - result = cv2.matchTemplate(screenshot_gray, resized, cv2.TM_CCOEFF_NORMED) + result = cv2.matchTemplate( + screenshot_gray, resized, cv2.TM_CCOEFF_NORMED + ) min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result) - + # 更新最佳匹配 if found is None or max_val > found[0]: found = (max_val, max_loc, scale) - + if found: max_val, max_loc, scale = found - h, w = int(template.shape[0] * scale), int(template.shape[1] * scale) + h, w = int(template.shape[0] * scale), int( + template.shape[1] * scale + ) if max_val >= threshold: logger.info(f"匹配到模板 {template_name},置信度: {max_val}") # 返回模板中心点坐标 @@ -93,26 +110,29 @@ def match_template(self, screenshot, template_name, threshold=0.9): center_y = max_loc[1] + h // 2 return True, (center_x, center_y) else: - logger.debug(f"未匹配到模板 {template_name},最高置信度: {max_val}") + logger.debug( + f"未匹配到模板 {template_name},最高置信度: {max_val}" + ) return False, (0, 0) else: logger.debug(f"未匹配到模板 {template_name}") return False, (0, 0) - - def restart_game(self): """重启游戏""" self._log(logging.INFO, "开始重启游戏") - + # 确定连接类型 - is_pc = hasattr(self.connector, 'hwnd') and self.connector.hwnd - is_adb = hasattr(self.connector, 'device_serial') and self.connector.device_serial - + is_pc = hasattr(self.connector, "hwnd") and self.connector.hwnd + is_adb = ( + hasattr(self.connector, "device_serial") + and self.connector.device_serial + ) + if not is_pc and not is_adb: self._log(logging.ERROR, "无法确定连接类型,无法重启游戏") return False - + # 关闭游戏进程 try: if is_pc: @@ -120,71 +140,101 @@ def restart_game(self): import win32gui import win32process import win32api - _, process_id = win32process.GetWindowThreadProcessId(self.connector.hwnd) + + _, process_id = win32process.GetWindowThreadProcessId( + self.connector.hwnd + ) process = win32api.OpenProcess(1, False, process_id) win32api.TerminateProcess(process, 0) win32api.CloseHandle(process) self._log(logging.INFO, "关闭游戏进程成功") else: # 对于ADB端,关闭游戏进程 - adb_path = getattr(self.connector, 'adb_path', 'adb') - subprocess.run(f"{adb_path} -s {self.connector.device_serial} shell am force-stop com.hypergryph.arknights", shell=True) + adb_path = getattr(self.connector, "adb_path", "adb") + subprocess.run( + f"{adb_path} -s {self.connector.device_serial} shell am force-stop com.hypergryph.arknights", + shell=True, + ) self._log(logging.INFO, "关闭游戏进程成功") except Exception as e: self._log(logging.ERROR, f"关闭游戏进程失败: {e}") return False - + # 等待一段时间后重新启动游戏 time.sleep(3) - + try: if is_pc: # 对于PC端,重新启动游戏 # 尝试从注册表获取游戏路径 try: import winreg - key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall") + + key = winreg.OpenKey( + winreg.HKEY_LOCAL_MACHINE, + r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", + ) for i in range(winreg.QueryInfoKey(key)[0]): subkey_name = winreg.EnumKey(key, i) subkey = winreg.OpenKey(key, subkey_name) try: - display_name = winreg.QueryValueEx(subkey, "DisplayName")[0] + display_name = winreg.QueryValueEx( + subkey, "DisplayName" + )[0] if "Arknights" in display_name: - install_location = winreg.QueryValueEx(subkey, "InstallLocation")[0] - game_path = Path(install_location) / "Arknights.exe" + install_location = winreg.QueryValueEx( + subkey, "InstallLocation" + )[0] + game_path = ( + Path(install_location) / "Arknights.exe" + ) if game_path.exists(): subprocess.Popen(str(game_path)) - self._log(logging.INFO, f"从注册表获取游戏路径并启动: {game_path}") + self._log( + logging.INFO, + f"从注册表获取游戏路径并启动: {game_path}", + ) break except: pass else: # 如果从注册表获取失败,使用默认路径 - game_path = Path("C:\\Program Files\\Arknights\\Arknights.exe") + game_path = Path( + "C:\\Program Files\\Arknights\\Arknights.exe" + ) if game_path.exists(): subprocess.Popen(str(game_path)) - self._log(logging.INFO, f"使用默认路径启动游戏: {game_path}") + self._log( + logging.INFO, + f"使用默认路径启动游戏: {game_path}", + ) else: self._log(logging.ERROR, "无法找到游戏可执行文件") return False except: # 如果注册表操作失败,使用默认路径 - game_path = Path("C:\\Program Files\\Arknights\\Arknights.exe") + game_path = Path( + "C:\\Program Files\\Arknights\\Arknights.exe" + ) if game_path.exists(): subprocess.Popen(str(game_path)) - self._log(logging.INFO, f"使用默认路径启动游戏: {game_path}") + self._log( + logging.INFO, f"使用默认路径启动游戏: {game_path}" + ) else: self._log(logging.ERROR, "无法找到游戏可执行文件") return False else: # 对于ADB端,重新启动游戏 - adb_path = getattr(self.connector, 'adb_path', 'adb') - subprocess.run(f"{adb_path} -s {self.connector.device_serial} shell am start -n com.hypergryph.arknights/com.u8.sdk.U8UnityContext") + adb_path = getattr(self.connector, "adb_path", "adb") + subprocess.run( + f"{adb_path} -s {self.connector.device_serial} shell am start -n com.hypergryph.arknights/com.u8.sdk.U8UnityContext" + ) self._log(logging.INFO, "重新启动游戏成功") except Exception as e: self._log(logging.ERROR, f"重新启动游戏失败: {e}") return False - + # 重新连接 try: self.connector.connect() @@ -194,19 +244,19 @@ def restart_game(self): self._log(logging.WARNING, "游戏重启后重新连接失败") except Exception as e: self._log(logging.ERROR, f"重新连接失败: {e}") - + return True def auto_login(self, first_start=False, stop_callback=None): """自动登录功能,处理服务器维护或掉线后的重新登录""" self._log(logging.INFO, "开始自动登录流程") - + def check_stop(): if stop_callback and not stop_callback(): self._log(logging.INFO, "检测到停止信号,中断登录流程") return False return True - + def sleep_with_check(seconds): """可中断的等待函数""" start_time = time.time() @@ -215,9 +265,12 @@ def sleep_with_check(seconds): return False time.sleep(0.1) return True - + # 确保连接器已连接 - if not hasattr(self.connector, 'is_connected') or not self.connector.is_connected: + if ( + not hasattr(self.connector, "is_connected") + or not self.connector.is_connected + ): logger.info("连接器未连接,尝试重新连接") try: self.connector.connect() @@ -227,27 +280,29 @@ def sleep_with_check(seconds): except Exception as e: logger.error(f"重新连接失败: {e}") return False - + # 首次启动时,先检测是否已经在游戏流程中 if first_start: logger.info("首次启动,检测是否已在游戏流程中") - + # 检查是否需要停止 if not check_stop(): return False - + screenshot = self.connector.capture_screenshot() if screenshot is not None: # 检查是否匹配到争锋频道入口 - matched, _ = self.match_template(screenshot, "competition_page", threshold=0.7) + matched, _ = self.match_template( + screenshot, "competition_page", threshold=0.7 + ) if matched: logger.info("已在争锋频道入口,无需登录") return True - + # 检查是否需要停止 if not check_stop(): return False - + # 检查是否匹配到0.png或1.png(加入赛事或开始游戏) try: # 尝试简单匹配 @@ -255,53 +310,75 @@ def sleep_with_check(seconds): # 检查是否需要停止 if not check_stop(): return False - - template_path = Path(f"images/process/{template_name}.png") + + template_path = process_image_path(template_name) if template_path.exists(): template = cv2.imread(str(template_path)) if template is not None: - screenshot_gray = cv2.cvtColor(screenshot, cv2.COLOR_BGR2GRAY) - template_gray = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY) - res = cv2.matchTemplate(screenshot_gray, template_gray, cv2.TM_CCOEFF_NORMED) + screenshot_gray = cv2.cvtColor( + screenshot, cv2.COLOR_BGR2GRAY + ) + template_gray = cv2.cvtColor( + template, cv2.COLOR_BGR2GRAY + ) + res = cv2.matchTemplate( + screenshot_gray, + template_gray, + cv2.TM_CCOEFF_NORMED, + ) _, max_val, _, _ = cv2.minMaxLoc(res) if max_val > 0.7: - logger.info(f"已在争锋频道页面,找到模板 {template_name}.png") + logger.info( + f"已在争锋频道页面,找到模板 {template_name}.png" + ) return True except Exception as e: logger.debug(f"检测争锋频道页面模板失败: {e}") - + # 检查是否需要停止 if not check_stop(): return False - + # 检查是否在战斗前准备阶段(PRE_BATTLE) # 匹配战斗前准备相关的模板(模板索引3,4,5,15) for template_name in ["3", "4", "5", "15"]: # 检查是否需要停止 if not check_stop(): return False - - template_path = Path(f"images/process/{template_name}.png") + + template_path = process_image_path(template_name) if template_path.exists(): try: template = cv2.imread(str(template_path)) if template is not None: - screenshot_gray = cv2.cvtColor(screenshot, cv2.COLOR_BGR2GRAY) - template_gray = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY) - res = cv2.matchTemplate(screenshot_gray, template_gray, cv2.TM_CCOEFF_NORMED) + screenshot_gray = cv2.cvtColor( + screenshot, cv2.COLOR_BGR2GRAY + ) + template_gray = cv2.cvtColor( + template, cv2.COLOR_BGR2GRAY + ) + res = cv2.matchTemplate( + screenshot_gray, + template_gray, + cv2.TM_CCOEFF_NORMED, + ) _, max_val, _, _ = cv2.minMaxLoc(res) if max_val > 0.7: - logger.info(f"已在战斗前准备阶段,找到模板 {template_name}.png,无需登录") + logger.info( + f"已在战斗前准备阶段,找到模板 {template_name}.png,无需登录" + ) return True except Exception as e: - logger.debug(f"检测战斗前准备模板 {template_name}.png 失败: {e}") - + logger.debug( + f"检测战斗前准备模板 {template_name}.png 失败: {e}" + ) + # 非首次启动时,等待游戏启动 if not first_start: self._log(logging.INFO, "重启游戏,等待游戏启动...") if not sleep_with_check(40): return False - + # 点击屏幕中心跳过中转页面(点击3次,每次间隔2秒) self._log(logging.INFO, "点击屏幕中心跳过中转页面") for i in range(3): @@ -310,7 +387,7 @@ def sleep_with_check(seconds): if i < 2: if not sleep_with_check(2): return False - + # 寻找并点击登录按钮 self._log(logging.INFO, "寻找登录按钮") login_button_found = False @@ -324,26 +401,31 @@ def sleep_with_check(seconds): if not sleep_with_check(1): return False continue - + h, w = screenshot.shape[:2] self._log(logging.DEBUG, f"截图尺寸: {w}x{h}") - - matched, pos = self.match_template(screenshot, "login_button", threshold=0.9) + + matched, pos = self.match_template( + screenshot, "login_button", threshold=0.9 + ) if matched: rel_x = pos[0] / w rel_y = pos[1] / h - self._log(logging.INFO, f"登录按钮位置: ({pos[0]}, {pos[1]}), 相对坐标: ({rel_x:.2f}, {rel_y:.2f})") + self._log( + logging.INFO, + f"登录按钮位置: ({pos[0]}, {pos[1]}), 相对坐标: ({rel_x:.2f}, {rel_y:.2f})", + ) self.connector.click((rel_x, rel_y)) self._log(logging.INFO, "点击登录按钮") login_button_found = True break if not sleep_with_check(1): return False - + if not login_button_found: self._log(logging.ERROR, "未找到登录按钮,登录流程中断") return False - + # 等待登录完成,最多等待70秒 self._log(logging.INFO, "等待登录完成 (最长70秒,动态检测)...") start_time = time.time() @@ -373,13 +455,15 @@ def sleep_with_check(seconds): # 寻找争锋频道入口,最多等待30秒 self._log(logging.INFO, "寻找争锋频道入口") start_time = time.time() - + # 尝试识别争锋频道 screenshot = self.connector.capture_screenshot() if screenshot is not None: - if self._check_and_click_competition_page(screenshot, sleep_with_check, check_stop): + if self._check_and_click_competition_page( + screenshot, sleep_with_check, check_stop + ): return True - + # 识别失败时点击右上角两次 self._log(logging.INFO, "未检测到争锋频道入口,点击屏幕右上角") for _ in range(2): @@ -390,7 +474,9 @@ def sleep_with_check(seconds): # 点击右上角后,立即再次检查争锋频道入口 screenshot = self.connector.capture_screenshot() if screenshot is not None: - if self._check_and_click_competition_page(screenshot, sleep_with_check, check_stop): + if self._check_and_click_competition_page( + screenshot, sleep_with_check, check_stop + ): return True # 同时检测两种关闭按钮五次(每次间隔2秒) @@ -423,7 +509,9 @@ def sleep_with_check(seconds): screenshot = self.connector.capture_screenshot() if screenshot is not None: - if self._check_and_click_competition_page(screenshot, sleep_with_check, check_stop): + if self._check_and_click_competition_page( + screenshot, sleep_with_check, check_stop + ): return True # 每次轮询间隔2秒,避免频繁截图造成性能浪费 @@ -433,15 +521,22 @@ def sleep_with_check(seconds): # 还是失败的话就重启 self._log(logging.ERROR, "未找到争锋频道入口,登录流程失败") return False - - def _check_and_click_competition_page(self, screenshot, sleep_with_check, check_stop): + + def _check_and_click_competition_page( + self, screenshot, sleep_with_check, check_stop + ): """争锋频道入口检测和点击方法""" - matched, pos = self.match_template(screenshot, "competition_page", threshold=0.7) + matched, pos = self.match_template( + screenshot, "competition_page", threshold=0.7 + ) if matched: h, w = screenshot.shape[:2] rel_x = pos[0] / w rel_y = pos[1] / h - self._log(logging.INFO, f"争锋频道入口位置: ({pos[0]}, {pos[1]}), 相对坐标: ({rel_x:.2f}, {rel_y:.2f})") + self._log( + logging.INFO, + f"争锋频道入口位置: ({pos[0]}, {pos[1]}), 相对坐标: ({rel_x:.2f}, {rel_y:.2f})", + ) self.connector.click((rel_x, rel_y)) self._log(logging.INFO, "点击进入争锋频道页面") if not sleep_with_check(2): @@ -449,7 +544,7 @@ def _check_and_click_competition_page(self, screenshot, sleep_with_check, check_ self._log(logging.INFO, "自动登录流程完成") return True return False - + def try_login_with_retry(self, max_wait_seconds=6, stop_callback=None): """尝试登录,如果未找到登录按钮则等待重试""" for i in range(max_wait_seconds): @@ -457,11 +552,13 @@ def try_login_with_retry(self, max_wait_seconds=6, stop_callback=None): if stop_callback and not stop_callback(): logger.info("检测到停止信号,取消登录尝试") return False - + screenshot = self.connector.capture_screenshot() if screenshot is not None: logger.info("获取截图成功,检查是否存在登录按钮") - matched, _ = self.match_template(screenshot, "login_button", threshold=0.9) + matched, _ = self.match_template( + screenshot, "login_button", threshold=0.9 + ) if matched: logger.info("找到登录按钮,执行登录流程") # 传递 stop_callback 给 auto_login @@ -472,7 +569,7 @@ def try_login_with_retry(self, max_wait_seconds=6, stop_callback=None): return False else: logger.info(f"第 {i+1} 次检查:未找到登录按钮,继续等待") - + # 可中断的等待 start_time = time.time() while time.time() - start_time < 6: @@ -481,27 +578,32 @@ def try_login_with_retry(self, max_wait_seconds=6, stop_callback=None): return False time.sleep(0.1) return False - + def restart_and_login(self, first_start=False, stop_callback=None): """重启游戏并尝试登录""" # 检查是否需要停止 if stop_callback and not stop_callback(): self._log(logging.INFO, "检测到停止信号,取消重启") return False - - self._log(logging.INFO, f"尝试重启游戏 (第 {self.restart_count + 1}/{self.max_restart_count} 次)") + + self._log( + logging.INFO, + f"尝试重启游戏 (第 {self.restart_count + 1}/{self.max_restart_count} 次)", + ) self.restart_count += 1 - + if self.restart_game(): self._log(logging.INFO, "游戏重启成功,尝试重新登录") - + # 检查是否需要停止 if stop_callback and not stop_callback(): self._log(logging.INFO, "检测到停止信号,取消登录") return False - + # 传递 stop_callback 给 auto_login - if self.auto_login(first_start=first_start, stop_callback=stop_callback): + if self.auto_login( + first_start=first_start, stop_callback=stop_callback + ): return True else: self._log(logging.ERROR, "重启后自动登录失败") @@ -509,20 +611,26 @@ def restart_and_login(self, first_start=False, stop_callback=None): else: self._log(logging.ERROR, "重启游戏失败") return False - + def auto_login_with_restart(self, first_start=False, stop_callback=None): """自动登录,失败时自动重启重试""" # 首先尝试直接登录(首次启动) - if self.auto_login(first_start=first_start, stop_callback=stop_callback): + if self.auto_login( + first_start=first_start, stop_callback=stop_callback + ): self.reset_restart_count() return True - + # 登录失败,尝试重启登录 for _ in range(self.max_restart_count - 1): - if not self.restart_and_login(first_start=False, stop_callback=stop_callback): + if not self.restart_and_login( + first_start=False, stop_callback=stop_callback + ): self._log(logging.WARNING, "重启登录失败,继续尝试") continue return True - - self._log(logging.ERROR, f"已尝试 {self.max_restart_count} 次,登录失败") - return False \ No newline at end of file + + self._log( + logging.ERROR, f"已尝试 {self.max_restart_count} 次,登录失败" + ) + return False diff --git a/maa_adb_connector.py b/src/game/maa_adb_connector.py similarity index 82% rename from maa_adb_connector.py rename to src/game/maa_adb_connector.py index b47bc9c..8c11a1c 100644 --- a/maa_adb_connector.py +++ b/src/game/maa_adb_connector.py @@ -15,9 +15,12 @@ def resolve_maafw_path() -> str: if os.environ.get("MAAFW_BINARY_PATH"): return os.environ["MAAFW_BINARY_PATH"] candidates = [ - Path(sys.executable).parent / "maafw", - Path.cwd() / "maafw", - Path(__file__).resolve().parent / "maafw", + Path(sys.executable).parent / "vendor" / "bin" / "maafw", + Path.cwd() / "vendor" / "bin" / "maafw", + Path(__file__).resolve().parent.parent.parent + / "vendor" + / "bin" + / "maafw", ] for p in candidates: if p.is_dir() and any(p.glob("MaaFramework.dll")): @@ -55,7 +58,7 @@ class InputMethodOption: @dataclass(frozen=True) class MaaConnectionConfig: maa_binary_path: str = "" - adb_path: str = r".\platform-tools\adb.exe" + adb_path: str = r".\vendor\bin\platform-tools\adb.exe" device_serial: str = "" screencap_method: int = 1 input_method: int = 4 @@ -139,12 +142,30 @@ def reset(cls): class ConnectionTypeRegistry: _types: list[ConnectionType] = [ - ConnectionType("adb", "ADB连接", "", "通用ADB连接,需手动指定设备地址"), - ConnectionType("ldplayer", "雷电模拟器", "emulator-5554", "雷电模拟器默认ADB地址"), - ConnectionType("mumu", "MuMu模拟器", "127.0.0.1:7555", "MuMu模拟器默认ADB地址"), - ConnectionType("mumu12", "MuMu12模拟器", "127.0.0.1:16384", "MuMu12模拟器默认ADB地址"), - ConnectionType("bluestacks", "蓝叠模拟器", "127.0.0.1:5555", "蓝叠模拟器默认ADB地址"), - ConnectionType("nox", "夜神模拟器", "127.0.0.1:62001", "夜神模拟器默认ADB地址"), + ConnectionType( + "adb", "ADB连接", "", "通用ADB连接,需手动指定设备地址" + ), + ConnectionType( + "ldplayer", "雷电模拟器", "emulator-5554", "雷电模拟器默认ADB地址" + ), + ConnectionType( + "mumu", "MuMu模拟器", "127.0.0.1:7555", "MuMu模拟器默认ADB地址" + ), + ConnectionType( + "mumu12", + "MuMu12模拟器", + "127.0.0.1:16384", + "MuMu12模拟器默认ADB地址", + ), + ConnectionType( + "bluestacks", + "蓝叠模拟器", + "127.0.0.1:5555", + "蓝叠模拟器默认ADB地址", + ), + ConnectionType( + "nox", "夜神模拟器", "127.0.0.1:62001", "夜神模拟器默认ADB地址" + ), ] @classmethod @@ -168,10 +189,24 @@ def get_type_by_id(cls, type_id: str) -> ConnectionType | None: class InputMethodRegistry: _methods: list[InputMethodOption] = [ - InputMethodOption("adb_shell", 1, "AdbShell", "ADB shell input命令,兼容性最高"), - InputMethodOption("minitouch_adb_key", 2, "MinitouchAndAdbKey", "minitouch注入+ADB按键,低延迟需root"), - InputMethodOption("maatouch", 4, "Maatouch", "Maatouch注入,低延迟MAA自带"), - InputMethodOption("emulator_extras", 8, "EmulatorExtras", "模拟器扩展接口,仅特定模拟器支持"), + InputMethodOption( + "adb_shell", 1, "AdbShell", "ADB shell input命令,兼容性最高" + ), + InputMethodOption( + "minitouch_adb_key", + 2, + "MinitouchAndAdbKey", + "minitouch注入+ADB按键,低延迟需root", + ), + InputMethodOption( + "maatouch", 4, "Maatouch", "Maatouch注入,低延迟MAA自带" + ), + InputMethodOption( + "emulator_extras", + 8, + "EmulatorExtras", + "模拟器扩展接口,仅特定模拟器支持", + ), ] @classmethod @@ -223,7 +258,9 @@ def connect(self): Toolkit.init_option(str(Path.cwd())) - target_serial = self.device_serial if self.device_serial else "127.0.0.1:5555" + target_serial = ( + self.device_serial if self.device_serial else "127.0.0.1:5555" + ) self.device_serial = target_serial adb_path = str(Path(self._config.adb_path).resolve()) @@ -236,18 +273,24 @@ def connect(self): ) self._ctrl.post_connection().wait() - self._ctrl.set_screenshot_use_raw_size(self._config.screenshot_use_raw_size) + self._ctrl.set_screenshot_use_raw_size( + self._config.screenshot_use_raw_size + ) self._ctrl.post_screencap().wait() image = self._ctrl.cached_image if image is not None: self.screen_height, self.screen_width = image.shape[:2] else: - self.screen_width, self.screen_height = self._get_window_size_fallback() + self.screen_width, self.screen_height = ( + self._get_window_size_fallback() + ) self.is_maa_available = True self.is_connected = True - logger.info(f"MAA Framework ADB连接成功: {target_serial}, 分辨率: {self.screen_width}x{self.screen_height}") + logger.info( + f"MAA Framework ADB连接成功: {target_serial}, 分辨率: {self.screen_width}x{self.screen_height}" + ) except Exception as e: self.is_maa_available = False @@ -257,7 +300,14 @@ def connect(self): def _get_window_size_fallback(self) -> tuple[int, int]: try: size_cmd = f"{self._config.adb_path} -s {self.device_serial} shell wm size" - result = subprocess.run(size_cmd, shell=True, capture_output=True, text=True, check=True, timeout=5) + result = subprocess.run( + size_cmd, + shell=True, + capture_output=True, + text=True, + check=True, + timeout=5, + ) output = result.stdout.strip() if "Physical size:" in output: res_str = output.split("Physical size: ")[1] @@ -296,7 +346,12 @@ def click(self, point: tuple[float, float]): except Exception as e: logger.error(f"MAA点击失败: {e}") - def swipe(self, start: tuple[float, float], end: tuple[float, float], duration: int = 500): + def swipe( + self, + start: tuple[float, float], + end: tuple[float, float], + duration: int = 500, + ): if not self.is_connected or self._ctrl is None: return try: @@ -306,14 +361,19 @@ def swipe(self, start: tuple[float, float], end: tuple[float, float], duration: y1_coord = int(y1 * self.screen_height) x2_coord = int(x2 * self.screen_width) y2_coord = int(y2 * self.screen_height) - logger.info(f"MAA滑动: ({x1_coord},{y1_coord}) -> ({x2_coord},{y2_coord})") - self._ctrl.post_swipe(x1_coord, y1_coord, x2_coord, y2_coord, duration).wait() + logger.info( + f"MAA滑动: ({x1_coord},{y1_coord}) -> ({x2_coord},{y2_coord})" + ) + self._ctrl.post_swipe( + x1_coord, y1_coord, x2_coord, y2_coord, duration + ).wait() except Exception as e: logger.error(f"MAA滑动失败: {e}") def get_device_list(self) -> list[str]: try: from maa.toolkit import AdbDevice + devices = AdbDevice.find() if devices: return [d.name for d in devices] @@ -321,7 +381,13 @@ def get_device_list(self) -> list[str]: logger.debug("MAA AdbDevice.find()失败,降级到subprocess") try: device_cmd = f"{self._config.adb_path} devices" - result = subprocess.run(device_cmd, shell=True, capture_output=True, text=True, timeout=5) + result = subprocess.run( + device_cmd, + shell=True, + capture_output=True, + text=True, + timeout=5, + ) devices = [] for line in result.stdout.split("\n"): if "\tdevice" in line: @@ -351,9 +417,10 @@ def set_config(self, config: MaaConnectionConfig): class AdbConnectorAdapter: - def __init__(self, adb_path: str = r".\platform-tools\adb.exe"): - import loadData - self._legacy_connector = loadData.AdbConnector() + def __init__(self, adb_path: str = r".\vendor\bin\platform-tools\adb.exe"): + from src.data import load_data + + self._legacy_connector = load_data.AdbConnector() self._maa_connector: MaaAdbConnector | None = None self._use_maa: bool = False self._maa_config = MaaConnectionConfig(adb_path=adb_path) @@ -456,7 +523,10 @@ def connect(self): if MaaFrameworkDetector.is_available(): try: maa_connector = MaaAdbConnector(self._maa_config) - maa_connector.device_serial = self._legacy_connector.device_serial or self._maa_config.device_serial + maa_connector.device_serial = ( + self._legacy_connector.device_serial + or self._maa_config.device_serial + ) maa_connector.connect() if maa_connector.is_connected: self._maa_connector = maa_connector @@ -487,7 +557,12 @@ def click(self, point: tuple[float, float]): else: self._legacy_connector.click(point) - def swipe(self, start: tuple[float, float], end: tuple[float, float], duration: int = 500): + def swipe( + self, + start: tuple[float, float], + end: tuple[float, float], + duration: int = 500, + ): if self._use_maa and self._maa_connector: self._maa_connector.swipe(start, end, duration) else: diff --git a/winrt_capture.py b/src/game/winrt_capture.py similarity index 90% rename from winrt_capture.py rename to src/game/winrt_capture.py index f69847d..38ef03e 100644 --- a/winrt_capture.py +++ b/src/game/winrt_capture.py @@ -11,6 +11,7 @@ - 运行时可调用 `recreate()` **切换捕获目标**(重新创建底层会话); - 通过锁保护 start/stop/recreate 的并发安全。 """ + from __future__ import annotations import threading import time @@ -22,10 +23,11 @@ import numpy as np from windows_capture import WindowsCapture, Frame, InternalCaptureControl -from PyQt6.QtWidgets import (QVBoxLayout,QListWidget,QLineEdit,QDialog) +from PyQt6.QtWidgets import QVBoxLayout, QListWidget, QLineEdit, QDialog logger = logging.getLogger(__name__) + class WinRTScreenCapture: """Windows.Graphics.Capture 适配器。 @@ -61,7 +63,9 @@ def __init__( # 控制状态 self._started = False - self._control: Optional[InternalCaptureControl] = None # 保存 CaptureControl,用于停止 + self._control: Optional[InternalCaptureControl] = ( + None # 保存 CaptureControl,用于停止 + ) # 保护 start/stop/recreate 的可重入锁 self._ctl_lock = threading.RLock() @@ -118,7 +122,9 @@ def start(self) -> None: cursor_capture=self._init_kwargs["cursor_capture"], draw_border=self._init_kwargs["draw_border"], secondary_window=None, - minimum_update_interval=self._init_kwargs["minimum_update_interval"], + minimum_update_interval=self._init_kwargs[ + "minimum_update_interval" + ], dirty_region=None, monitor_index=1, window_name=None, @@ -145,7 +151,11 @@ def stop(self) -> None: with self._lock: self._latest = None - def recreate(self, window_name: Optional[str] = None, monitor_index: Optional[int] = None) -> None: + def recreate( + self, + window_name: Optional[str] = None, + monitor_index: Optional[int] = None, + ) -> None: """运行时切换捕获目标并立即启动。 同步步骤:stop → 以新参数创建 → 重新绑定事件 → 启动。 @@ -156,7 +166,9 @@ def recreate(self, window_name: Optional[str] = None, monitor_index: Optional[in cursor_capture=self._init_kwargs["cursor_capture"], draw_border=self._init_kwargs["draw_border"], secondary_window=None, - minimum_update_interval=self._init_kwargs["minimum_update_interval"], + minimum_update_interval=self._init_kwargs[ + "minimum_update_interval" + ], dirty_region=None, monitor_index=monitor_index if (window_name is None) else None, window_name=window_name, @@ -194,7 +206,9 @@ def wait_first_frame(self, timeout_sec: float = 3.0) -> bool: time.sleep(0.01) return False - def snapshot(self, bbox: Optional[tuple[int, int, int, int]] = None) -> np.ndarray: + def snapshot( + self, bbox: Optional[tuple[int, int, int, int]] = None + ) -> np.ndarray: """返回最近一帧的 **BGR** 拷贝图像,可选裁剪到指定 bbox。 若尚未有首帧,将抛出 `RuntimeError`。 @@ -202,7 +216,7 @@ def snapshot(self, bbox: Optional[tuple[int, int, int, int]] = None) -> np.ndarr with self._lock: if self._latest is None: raise RuntimeError("WinRT capture 尚未产生首帧") - + frame = self._latest.copy() if bbox: x1, y1, x2, y2 = bbox @@ -214,7 +228,11 @@ def snapshot(self, bbox: Optional[tuple[int, int, int, int]] = None) -> np.ndarr return frame[y1:y2, x1:x2] return frame - def snapshot_once(self, bbox: Optional[tuple[int, int, int, int]] = None, timeout_sec: float = 2.0) -> np.ndarray: + def snapshot_once( + self, + bbox: Optional[tuple[int, int, int, int]] = None, + timeout_sec: float = 2.0, + ) -> np.ndarray: """返回最近一帧的 **BGR** 拷贝图像,可选裁剪到指定 bbox。 若当前未启动,则会临时启动捕获,获取一帧后立即停止。 @@ -223,7 +241,7 @@ def snapshot_once(self, bbox: Optional[tuple[int, int, int, int]] = None, timeou was_started = self._started if not was_started: self.start() - + frame = None t0 = time.time() while time.time() - t0 < timeout_sec: @@ -232,8 +250,8 @@ def snapshot_once(self, bbox: Optional[tuple[int, int, int, int]] = None, timeou frame = self._latest.copy() break time.sleep(0.01) - - if frame is None: # 超时 + + if frame is None: # 超时 if not was_started: self.stop() raise RuntimeError("WinRT capture 尚未产生首帧") @@ -251,7 +269,11 @@ def snapshot_once(self, bbox: Optional[tuple[int, int, int, int]] = None, timeou self.stop() return frame - def set_capture_target(self, window_name: Optional[str] = None, monitor_index: Optional[int] = None) -> None: + def set_capture_target( + self, + window_name: Optional[str] = None, + monitor_index: Optional[int] = None, + ) -> None: """ 设置 WinRT 截屏目标(窗口标题或整屏),并启动捕获,等待首帧。 若初始化失败,将抛出异常。 @@ -259,11 +281,15 @@ def set_capture_target(self, window_name: Optional[str] = None, monitor_index: O with self._ctl_lock: if self._started: # 如果已经启动,则重建底层会话 - self.recreate(window_name=window_name, monitor_index=monitor_index) + self.recreate( + window_name=window_name, monitor_index=monitor_index + ) else: # 否则,更新初始化参数并启动 self._init_kwargs.update( - monitor_index=monitor_index if (window_name is None) else None, + monitor_index=( + monitor_index if (window_name is None) else None + ), window_name=window_name, ) self._cap = WindowsCapture(**self._init_kwargs) @@ -276,10 +302,13 @@ def set_capture_target(self, window_name: Optional[str] = None, monitor_index: O self.stop() raise RuntimeError("WinRT capture 尚未产生首帧") + def list_visible_window_titles() -> list[str]: """列出所有**可见**窗口的标题(去重并按字典序排序)。""" EnumWindows = ctypes.windll.user32.EnumWindows - EnumWindowsProc = ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.HWND, wintypes.LPARAM) + EnumWindowsProc = ctypes.WINFUNCTYPE( + wintypes.BOOL, wintypes.HWND, wintypes.LPARAM + ) IsWindowVisible = ctypes.windll.user32.IsWindowVisible GetWindowTextW = ctypes.windll.user32.GetWindowTextW GetWindowTextLengthW = ctypes.windll.user32.GetWindowTextLengthW @@ -302,6 +331,7 @@ def foreach(hwnd, lParam): logger.info(f"窗口列表:{titles}") return titles + # ------------------------- 截屏源选择对话框 ------------------------- class WindowPickerDialog(QDialog): """列出可见窗口标题,并内置“整屏(1/2/3)”选项。双击确定。""" diff --git a/src/models/__init__.py b/src/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/models/model.py b/src/models/model.py similarity index 94% rename from models/model.py rename to src/models/model.py index c55a035..69ad1e4 100644 --- a/models/model.py +++ b/src/models/model.py @@ -1,9 +1,12 @@ import torch import torch.nn as nn -from config import FIELD_FEATURE_COUNT, MONSTER_COUNT +from src.core.config import FIELD_FEATURE_COUNT, MONSTER_COUNT + class UnitAwareTransformer(nn.Module): - def __init__(self, num_units, embed_dim=256, num_heads=4, num_layers=4, dropout=0.3): + def __init__( + self, num_units, embed_dim=256, num_heads=4, num_layers=4, dropout=0.3 + ): super().__init__() # num_units,包括怪物种类和场地特征种类 # 怪物特征 + 场地特征 = 总特征数量 @@ -67,13 +70,12 @@ def __init__(self, num_units, embed_dim=256, num_heads=4, num_layers=4, dropout= nn.init.xavier_uniform_(self.friend_attentions[-1].in_proj_weight) self.norm.append(nn.LayerNorm(embed_dim)) - # 全连接输出层 self.fc = nn.Sequential( nn.Linear(embed_dim, embed_dim * 2), nn.ReLU(), nn.Dropout(dropout), - nn.Linear(embed_dim * 2, 1) + nn.Linear(embed_dim * 2, 1), ) def forward(self, left_signs, left_counts, right_signs, right_counts): @@ -94,7 +96,7 @@ def forward(self, left_signs, left_counts, right_signs, right_counts): left_feat = torch.cat( [ left_feat[..., : embed_dim // 2], # 前x维 - left_feat[..., embed_dim // 2:] + left_feat[..., embed_dim // 2 :] * left_values.unsqueeze(-1), # 后y维乘数量 ], dim=-1, @@ -102,7 +104,7 @@ def forward(self, left_signs, left_counts, right_signs, right_counts): right_feat = torch.cat( [ right_feat[..., : embed_dim // 2], - right_feat[..., embed_dim // 2:] * right_values.unsqueeze(-1), + right_feat[..., embed_dim // 2 :] * right_values.unsqueeze(-1), ], dim=-1, ) diff --git a/models/muon.py b/src/models/muon.py similarity index 75% rename from models/muon.py rename to src/models/muon.py index 0e87c93..1b59250 100644 --- a/models/muon.py +++ b/src/models/muon.py @@ -1,5 +1,4 @@ import torch -import torch.optim as optim class Lion(torch.optim.Optimizer): @@ -28,26 +27,26 @@ def step(self, closure=None): loss = closure() for group in self.param_groups: - for p in group['params']: + for p in group["params"]: if p.grad is None: continue # 首先执行 Weight Decay - p.data.mul_(1 - group['lr'] * group['weight_decay']) + p.data.mul_(1 - group["lr"] * group["weight_decay"]) grad = p.grad state = self.state[p] # 初始化动量状态 if len(state) == 0: - state['exp_avg'] = torch.zeros_like(p) + state["exp_avg"] = torch.zeros_like(p) - exp_avg = state['exp_avg'] - beta1, beta2 = group['betas'] + exp_avg = state["exp_avg"] + beta1, beta2 = group["betas"] # 计算 Lion 特有的更新:符号动量 update = exp_avg * beta1 + grad * (1 - beta1) - p.add_(torch.sign(update), alpha=-group['lr']) + p.add_(torch.sign(update), alpha=-group["lr"]) # 更新 EMA 动量 exp_avg.mul_(beta2).add_(grad, alpha=1 - beta2) @@ -60,8 +59,15 @@ class Muon(torch.optim.Optimizer): 根据《Muon is Scalable for LLM Training》技术报告实现的 Muon 优化器。 """ - def __init__(self, params, lr=1e-3, momentum=0.95, weight_decay=0.1, ns_steps=5): - defaults = dict(lr=lr, momentum=momentum, weight_decay=weight_decay, ns_steps=ns_steps) + def __init__( + self, params, lr=1e-3, momentum=0.95, weight_decay=0.1, ns_steps=5 + ): + defaults = dict( + lr=lr, + momentum=momentum, + weight_decay=weight_decay, + ns_steps=ns_steps, + ) super().__init__(params, defaults) @torch.no_grad() @@ -72,23 +78,23 @@ def step(self, closure=None): loss = closure() for group in self.param_groups: - lr = group['lr'] - momentum = group['momentum'] - weight_decay = group['weight_decay'] - ns_steps = group['ns_steps'] + lr = group["lr"] + momentum = group["momentum"] + weight_decay = group["weight_decay"] + ns_steps = group["ns_steps"] - for p in group['params']: + for p in group["params"]: if p.grad is None: continue grad = p.grad if grad.is_sparse: - raise RuntimeError('Muon 不支持稀疏梯度计算') + raise RuntimeError("Muon 不支持稀疏梯度计算") state = self.state[p] if len(state) == 0: - state['momentum_buffer'] = torch.zeros_like(grad) + state["momentum_buffer"] = torch.zeros_like(grad) - buf = state['momentum_buffer'] + buf = state["momentum_buffer"] # 应用基于 Nesterov 的动量: M_t = \mu * M_{t-1} + \nabla L_t buf.mul_(momentum).add_(grad) @@ -118,7 +124,7 @@ def step(self, closure=None): dim0, dim1 = X.size(0), X.size(1) if X.ndim > 1 else 1 max_dim = max(dim0, dim1) - scale = 0.2 * (max_dim ** 0.5) + scale = 0.2 * (max_dim**0.5) p.data.mul_(1 - lr * weight_decay) p.data.add_(X, alpha=-lr * scale) @@ -126,7 +132,9 @@ def step(self, closure=None): return loss -def get_muon_lion_optimizers(model, muon_lr, lion_lr, weight_decay=0.1, muon_momentum=0.95): +def get_muon_lion_optimizers( + model, muon_lr, lion_lr, weight_decay=0.1, muon_momentum=0.95 +): """ 提供 Muon + Lion 的组合优化器分发策略。 对模型的所有 >=2D 参数采用 Muon; @@ -140,12 +148,17 @@ def get_muon_lion_optimizers(model, muon_lr, lion_lr, weight_decay=0.1, muon_mom if not p.requires_grad: continue - if p.ndim >= 2 and 'embed' not in name.lower(): + if p.ndim >= 2 and "embed" not in name.lower(): muon_params.append(p) else: lion_params.append(p) - muon_opt = Muon(muon_params, lr=muon_lr, momentum=muon_momentum, weight_decay=weight_decay) + muon_opt = Muon( + muon_params, + lr=muon_lr, + momentum=muon_momentum, + weight_decay=weight_decay, + ) # Lion 需要稍微大一点的 weight_decay 以防止过拟合,通常建议比 AdamW 大 3-10 倍 # 这里我们将其设置为常规 weight_decay 的 3 倍 diff --git a/predict.py b/src/models/predict.py similarity index 61% rename from predict.py rename to src/models/predict.py index 59edb44..8e14cc4 100644 --- a/predict.py +++ b/src/models/predict.py @@ -1,4 +1,6 @@ import re +import sys +import importlib from datetime import datetime from functools import cache from pathlib import Path @@ -7,11 +9,37 @@ import torch import logging -from config import MONSTER_COUNT -from config import FIELD_FEATURE_COUNT +from src.core.config import MONSTER_COUNT +from src.core.config import FIELD_FEATURE_COUNT logger = logging.getLogger(__name__) + +def _register_legacy_model_aliases(): + """ + 为旧版 pickle 模型提供模块路径兼容: + 例如历史权重里记录的是 `models.model`,而当前代码已迁移到 `src.models.model`。 + """ + legacy_to_new = { + "models": "src.models", + "models.model": "src.models.model", + "models.muon": "src.models.muon", + } + + for legacy_name, new_name in legacy_to_new.items(): + if legacy_name in sys.modules: + continue + try: + sys.modules[legacy_name] = importlib.import_module(new_name) + except Exception as e: + logger.debug( + "兼容模块别名注册失败: %s -> %s, %s", + legacy_name, + new_name, + e, + ) + + def get_device(prefer_gpu=True): """ prefer_gpu (bool): 是否优先尝试使用GPU @@ -20,7 +48,10 @@ def get_device(prefer_gpu=True): if torch.cuda.is_available(): logger.info("Use torch with cuda") return torch.device("cuda") - elif hasattr(torch.backends, "mps") and torch.backends.mps.is_available(): + elif ( + hasattr(torch.backends, "mps") + and torch.backends.mps.is_available() + ): logger.info("Use torch with mps") return torch.device("mps") # Apple Silicon GPU elif hasattr(torch, "xpu") and torch.xpu.is_available(): # Intel GPU @@ -29,6 +60,7 @@ def get_device(prefer_gpu=True): logger.info("Use torch with cpu") return torch.device("cpu") + class CannotModel: def __init__(self, model_path="models"): self.device = get_device() @@ -49,7 +81,11 @@ def _resolve_model_path(self, path): if Path(path).is_dir(): logger.info(f"Searching for the latest model in directory: {path}") model_dir = Path(path) - models = [f for f in model_dir.iterdir() if f.suffix == ".pth" and f.is_file()] + models = [ + f + for f in model_dir.iterdir() + if f.suffix == ".pth" and f.is_file() + ] if not models: logger.error(f"No model files (.pth) found in {path}") @@ -64,12 +100,20 @@ def _resolve_model_path(self, path): match = pattern.match(model_file_path.name) if match: model_type = match.group(1) - timestamp_str = match.group(2) # Group 2 captures the timestamp + timestamp_str = match.group( + 2 + ) # Group 2 captures the timestamp try: model_time = datetime.strptime( timestamp_str, "%Y_%m_%d_%H_%M_%S" ) - valid_models.append((model_time, priority.get(model_type, 3), model_file_path)) + valid_models.append( + ( + model_time, + priority.get(model_type, 3), + model_file_path, + ) + ) except ValueError: continue # Ignore files with malformed timestamps @@ -99,6 +143,9 @@ def load_model(self): rf"未找到训练好的模型文件 {self.model_path},请先训练模型" ) + # 兼容项目重构前保存的整模型 pickle 路径 + _register_legacy_model_aliases() + try: model = torch.load( self.model_path, @@ -106,9 +153,7 @@ def load_model(self): weights_only=False, ) except TypeError: # 如果旧版本 PyTorch 不认识 weights_only - model = torch.load( - self.model_path, map_location=self.device - ) + model = torch.load(self.model_path, map_location=self.device) model.eval() self.model = model.to(self.device) @@ -117,16 +162,20 @@ def load_model(self): if "missing keys" in str(e): error_msg += "\n可能是模型结构不匹配,请重新训练模型" raise e # 无法继续运行,退出程序 - - def export_onnx(self,outputpath, monster_count=MONSTER_COUNT): + + def export_onnx(self, outputpath, monster_count=MONSTER_COUNT): # 确保模型在 CPU 上(避免设备不一致) self.model = self.model.cpu() self.model.eval() # 生成虚拟输入(与模型同设备) device = next(self.model.parameters()).device - dummy_left_counts = torch.randint(0, 10, (1, monster_count), dtype=torch.int16, device=device) - dummy_right_counts = torch.randint(0, 10, (1, monster_count), dtype=torch.int16, device=device) + dummy_left_counts = torch.randint( + 0, 10, (1, monster_count), dtype=torch.int16, device=device + ) + dummy_right_counts = torch.randint( + 0, 10, (1, monster_count), dtype=torch.int16, device=device + ) # 获取符号和绝对值张量(确保在相同设备) left_signs = torch.sign(dummy_left_counts.to(torch.int64)).to(device) @@ -135,9 +184,14 @@ def export_onnx(self,outputpath, monster_count=MONSTER_COUNT): right_counts = torch.abs(dummy_right_counts.to(torch.int64)).to(device) # 导出参数 - input_names = ["left_signs", "left_counts", "right_signs", "right_counts"] - dynamic_axes = {name: {0: 'batch_size'} for name in input_names} - dynamic_axes["output"] = {0: 'batch_size'} + input_names = [ + "left_signs", + "left_counts", + "right_signs", + "right_counts", + ] + dynamic_axes = {name: {0: "batch_size"} for name in input_names} + dynamic_axes["output"] = {0: "batch_size"} # 导出 ONNX torch.onnx.export( @@ -148,10 +202,14 @@ def export_onnx(self,outputpath, monster_count=MONSTER_COUNT): output_names=["output"], dynamic_axes=dynamic_axes, opset_version=20, - verbose=True # 开启详细输出便于调试 + verbose=True, # 开启详细输出便于调试 ) - def get_prediction(self, left_counts: np.typing.ArrayLike, right_counts: np.typing.ArrayLike): + def get_prediction( + self, + left_counts: np.typing.ArrayLike, + right_counts: np.typing.ArrayLike, + ): if self.model is None: raise RuntimeError("模型未正确初始化") @@ -201,20 +259,34 @@ def get_prediction_with_terrain(self, full_features: np.typing.ArrayLike): raise RuntimeError("模型未正确初始化") # 检查特征向量长度 - expected_length = MONSTER_COUNT * 2 + FIELD_FEATURE_COUNT * 2 # 77L + 6L + 77R + 6R = 166 + expected_length = ( + MONSTER_COUNT * 2 + FIELD_FEATURE_COUNT * 2 + ) # 77L + 6L + 77R + 6R = 166 if len(full_features) != expected_length: - logger.warning(f"特征向量长度不匹配: 期望{expected_length}, 实际{len(full_features)}") + logger.warning( + f"特征向量长度不匹配: 期望{expected_length}, 实际{len(full_features)}" + ) # 如果长度不匹配,回退到原始方法 left_counts = full_features[:MONSTER_COUNT] - right_counts = full_features[MONSTER_COUNT:MONSTER_COUNT*2] + right_counts = full_features[MONSTER_COUNT : MONSTER_COUNT * 2] return self.get_prediction(left_counts, right_counts) # 提取各个部分 left_monsters = full_features[:MONSTER_COUNT] # 1L-77L - left_terrain = full_features[MONSTER_COUNT:MONSTER_COUNT+FIELD_FEATURE_COUNT] # 78L-83L - right_monsters = full_features[MONSTER_COUNT+FIELD_FEATURE_COUNT:MONSTER_COUNT*2+FIELD_FEATURE_COUNT] # 1R-77R - right_terrain = full_features[MONSTER_COUNT*2+FIELD_FEATURE_COUNT:MONSTER_COUNT*2+FIELD_FEATURE_COUNT*2] # 78R-83R - + left_terrain = full_features[ + MONSTER_COUNT : MONSTER_COUNT + FIELD_FEATURE_COUNT + ] # 78L-83L + right_monsters = full_features[ + MONSTER_COUNT + + FIELD_FEATURE_COUNT : MONSTER_COUNT * 2 + + FIELD_FEATURE_COUNT + ] # 1R-77R + right_terrain = full_features[ + MONSTER_COUNT * 2 + + FIELD_FEATURE_COUNT : MONSTER_COUNT * 2 + + FIELD_FEATURE_COUNT * 2 + ] # 78R-83R + # 合并怪物特征和地形特征(按照训练时的格式) left_counts = np.concatenate([left_monsters, left_terrain]) right_counts = np.concatenate([right_monsters, right_terrain]) @@ -222,27 +294,62 @@ def get_prediction_with_terrain(self, full_features: np.typing.ArrayLike): # 转换为张量并处理符号和绝对值 # 对于怪物特征,使用符号和绝对值 # 对于地形特征,不需要符号处理(地形特征本身就是0/1值) - left_monster_signs = torch.sign(torch.tensor(left_monsters, dtype=torch.int16)) - left_terrain_signs = torch.ones_like(torch.tensor(left_terrain, dtype=torch.int16)) # 地形特征符号为1 - left_signs = torch.cat([left_monster_signs, left_terrain_signs]).unsqueeze(0).to(self.device) - - left_monster_counts = torch.abs(torch.tensor(left_monsters, dtype=torch.int16)) - left_terrain_counts = torch.tensor(left_terrain, dtype=torch.int16) # 地形特征直接使用原值 - left_counts_tensor = torch.cat([left_monster_counts, left_terrain_counts]).unsqueeze(0).to(self.device) - - right_monster_signs = torch.sign(torch.tensor(right_monsters, dtype=torch.int16)) - right_terrain_signs = torch.ones_like(torch.tensor(right_terrain, dtype=torch.int16)) # 地形特征符号为1 - right_signs = torch.cat([right_monster_signs, right_terrain_signs]).unsqueeze(0).to(self.device) - - right_monster_counts = torch.abs(torch.tensor(right_monsters, dtype=torch.int16)) - right_terrain_counts = torch.tensor(right_terrain, dtype=torch.int16) # 地形特征直接使用原值 - right_counts_tensor = torch.cat([right_monster_counts, right_terrain_counts]).unsqueeze(0).to(self.device) + left_monster_signs = torch.sign( + torch.tensor(left_monsters, dtype=torch.int16) + ) + left_terrain_signs = torch.ones_like( + torch.tensor(left_terrain, dtype=torch.int16) + ) # 地形特征符号为1 + left_signs = ( + torch.cat([left_monster_signs, left_terrain_signs]) + .unsqueeze(0) + .to(self.device) + ) + + left_monster_counts = torch.abs( + torch.tensor(left_monsters, dtype=torch.int16) + ) + left_terrain_counts = torch.tensor( + left_terrain, dtype=torch.int16 + ) # 地形特征直接使用原值 + left_counts_tensor = ( + torch.cat([left_monster_counts, left_terrain_counts]) + .unsqueeze(0) + .to(self.device) + ) + + right_monster_signs = torch.sign( + torch.tensor(right_monsters, dtype=torch.int16) + ) + right_terrain_signs = torch.ones_like( + torch.tensor(right_terrain, dtype=torch.int16) + ) # 地形特征符号为1 + right_signs = ( + torch.cat([right_monster_signs, right_terrain_signs]) + .unsqueeze(0) + .to(self.device) + ) + + right_monster_counts = torch.abs( + torch.tensor(right_monsters, dtype=torch.int16) + ) + right_terrain_counts = torch.tensor( + right_terrain, dtype=torch.int16 + ) # 地形特征直接使用原值 + right_counts_tensor = ( + torch.cat([right_monster_counts, right_terrain_counts]) + .unsqueeze(0) + .to(self.device) + ) # 预测流程 with torch.no_grad(): # 使用修改后的模型前向传播流程,现在包含地形特征 prediction = self.model( - left_signs, left_counts_tensor, right_signs, right_counts_tensor + left_signs, + left_counts_tensor, + right_signs, + right_counts_tensor, ).item() # 确保预测值在有效范围内 diff --git a/predict_onnx.py b/src/models/predict_onnx.py similarity index 74% rename from predict_onnx.py rename to src/models/predict_onnx.py index 31a527f..6ae999d 100644 --- a/predict_onnx.py +++ b/src/models/predict_onnx.py @@ -5,11 +5,12 @@ import numpy as np import logging -from config import MONSTER_COUNT -from config import FIELD_FEATURE_COUNT +from src.core.config import MONSTER_COUNT +from src.core.config import FIELD_FEATURE_COUNT logger = logging.getLogger(__name__) + class CannotModel: def __init__(self, model_path="models"): self.model_path = self._resolve_model_path(model_path) @@ -35,7 +36,7 @@ def _resolve_model_path(self, path): if default_path.exists(): logger.info(f"Found default model: {default_path}") return str(default_path) - + logger.error(f"No valid ONNX model files found in {path}") return str(default_path) @@ -50,36 +51,42 @@ def load_model(self): """加载 ONNX 模型""" try: if not os.path.exists(self.model_path): - raise FileNotFoundError(f"未找到 ONNX 模型文件 {self.model_path}") - + raise FileNotFoundError( + f"未找到 ONNX 模型文件 {self.model_path}" + ) + # 配置会话选项 sess_options = ort.SessionOptions() - sess_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL - + sess_options.graph_optimization_level = ( + ort.GraphOptimizationLevel.ORT_ENABLE_ALL + ) + # 创建会话(默认使用 CPU) self.session = ort.InferenceSession( self.model_path, sess_options, - providers=['CPUExecutionProvider'] + providers=["CPUExecutionProvider"], ) - + except Exception as e: raise RuntimeError(f"ONNX 模型加载失败: {str(e)}") - def get_prediction(self, left_counts: np.ndarray, right_counts: np.ndarray): + def get_prediction( + self, left_counts: np.ndarray, right_counts: np.ndarray + ): if self.session is None: raise RuntimeError("模型未正确初始化") - + def validate_input(arr): """验证并转换输入数据""" # 转换为 int64 类型 arr = arr.astype(np.int64) - + # 添加批次维度(如果输入是单样本) if arr.ndim == 1: arr = arr[np.newaxis, :] # shape: (1, 56) return arr - + # 处理符号和绝对值,以匹配导出的模型输入 left_signs_arr = np.sign(left_counts).astype(np.int64) left_counts_arr = np.abs(left_counts).astype(np.int64) @@ -90,47 +97,60 @@ def validate_input(arr): "left_signs": validate_input(left_signs_arr), "left_counts": validate_input(left_counts_arr), "right_signs": validate_input(right_signs_arr), - "right_counts": validate_input(right_counts_arr) + "right_counts": validate_input(right_counts_arr), } - + # 执行推理 try: output = self.session.run( - output_names=["output"], - input_feed=inputs + output_names=["output"], input_feed=inputs ) # output 是一个列表,output[0] 是形状为 (batch_size, 1) 的数组 prediction = output[0].flatten()[0] except Exception as e: raise RuntimeError(f"推理失败: {str(e)}") - + # 后处理(与原逻辑一致) if np.isnan(prediction) or np.isinf(prediction): logger.warning("警告: 预测结果包含NaN或Inf,返回默认值0.5") prediction = 0.5 - + prediction = np.clip(prediction, 0.0, 1.0) return float(prediction) - + def get_prediction_with_terrain(self, full_features: np.ndarray): """使用包含地形特征的完整特征向量进行预测(ONNX版本)""" if self.session is None: raise RuntimeError("模型未正确初始化") # 检查特征向量长度 - expected_length = MONSTER_COUNT * 2 + FIELD_FEATURE_COUNT * 2 # 77L + 6L + 77R + 6R = 166 + expected_length = ( + MONSTER_COUNT * 2 + FIELD_FEATURE_COUNT * 2 + ) # 77L + 6L + 77R + 6R = 166 if len(full_features) != expected_length: - logger.warning(f"特征向量长度不匹配: 期望{expected_length}, 实际{len(full_features)}") + logger.warning( + f"特征向量长度不匹配: 期望{expected_length}, 实际{len(full_features)}" + ) # 如果长度不匹配,回退到原始方法 left_counts = full_features[:MONSTER_COUNT] - right_counts = full_features[MONSTER_COUNT:MONSTER_COUNT*2] + right_counts = full_features[MONSTER_COUNT : MONSTER_COUNT * 2] return self.get_prediction(left_counts, right_counts) # 提取各个部分 left_monsters = full_features[:MONSTER_COUNT] # 1L-77L - left_terrain = full_features[MONSTER_COUNT:MONSTER_COUNT+FIELD_FEATURE_COUNT] # 78L-83L - right_monsters = full_features[MONSTER_COUNT+FIELD_FEATURE_COUNT:MONSTER_COUNT*2+FIELD_FEATURE_COUNT] # 1R-77R - right_terrain = full_features[MONSTER_COUNT*2+FIELD_FEATURE_COUNT:MONSTER_COUNT*2+FIELD_FEATURE_COUNT*2] # 78R-83R + left_terrain = full_features[ + MONSTER_COUNT : MONSTER_COUNT + FIELD_FEATURE_COUNT + ] # 78L-83L + right_monsters = full_features[ + MONSTER_COUNT + + FIELD_FEATURE_COUNT : MONSTER_COUNT * 2 + + FIELD_FEATURE_COUNT + ] # 1R-77R + right_terrain = full_features[ + MONSTER_COUNT * 2 + + FIELD_FEATURE_COUNT : MONSTER_COUNT * 2 + + FIELD_FEATURE_COUNT * 2 + ] # 78R-83R # 处理左侧特征 left_monster_signs = np.sign(left_monsters).astype(np.int64) @@ -138,15 +158,21 @@ def get_prediction_with_terrain(self, full_features: np.ndarray): left_signs = np.concatenate([left_monster_signs, left_terrain_signs]) left_monster_counts = np.abs(left_monsters).astype(np.int64) - left_counts = np.concatenate([left_monster_counts, left_terrain.astype(np.int64)]) + left_counts = np.concatenate( + [left_monster_counts, left_terrain.astype(np.int64)] + ) # 处理右侧特征 right_monster_signs = np.sign(right_monsters).astype(np.int64) right_terrain_signs = np.ones_like(right_terrain).astype(np.int64) - right_signs = np.concatenate([right_monster_signs, right_terrain_signs]) + right_signs = np.concatenate( + [right_monster_signs, right_terrain_signs] + ) right_monster_counts = np.abs(right_monsters).astype(np.int64) - right_counts = np.concatenate([right_monster_counts, right_terrain.astype(np.int64)]) + right_counts = np.concatenate( + [right_monster_counts, right_terrain.astype(np.int64)] + ) def validate_input(arr): """验证并转换输入数据""" @@ -159,14 +185,13 @@ def validate_input(arr): "left_signs": validate_input(left_signs), "left_counts": validate_input(left_counts), "right_signs": validate_input(right_signs), - "right_counts": validate_input(right_counts) + "right_counts": validate_input(right_counts), } # 执行推理 try: output = self.session.run( - output_names=["output"], - input_feed=inputs + output_names=["output"], input_feed=inputs ) # output[0] 是形状为 (batch_size, 1) 的数组 prediction = output[0].flatten()[0] @@ -179,4 +204,4 @@ def validate_input(arr): prediction = 0.5 prediction = np.clip(prediction, 0.0, 1.0) - return float(prediction) \ No newline at end of file + return float(prediction) diff --git a/train.py b/src/models/train.py similarity index 70% rename from train.py rename to src/models/train.py index 81549b0..42a5654 100644 --- a/train.py +++ b/src/models/train.py @@ -2,6 +2,7 @@ from functools import cache from datetime import datetime from pathlib import Path +import sys import matplotlib.pyplot as plt import numpy as np @@ -11,11 +12,22 @@ import torch.optim as optim from sklearn.model_selection import train_test_split from torch.utils.data import DataLoader, Dataset -from config import FIELD_FEATURE_COUNT, MONSTER_COUNT -# 导入拆分到 models 文件夹中的模型和 Muon 优化器方法 -from models.model import UnitAwareTransformer -from models.muon import get_muon_lion_optimizers +# Handle both direct execution and module import +if __name__ == "__main__": + # Direct execution: add parent to path + sys.path.insert(0, str(Path(__file__).parent.parent.parent)) + from src.core.config import FIELD_FEATURE_COUNT, MONSTER_COUNT + from src.models.model import UnitAwareTransformer + from src.models.muon import get_muon_lion_optimizers + from src.core.paths import PROJECT_ROOT +else: + # Module import + from ..core.config import FIELD_FEATURE_COUNT, MONSTER_COUNT + from .model import UnitAwareTransformer + from .muon import get_muon_lion_optimizers + from ..core.paths import PROJECT_ROOT + print(f"场地特征数量: {FIELD_FEATURE_COUNT}") @@ -31,7 +43,10 @@ def get_device(prefer_gpu=True): if prefer_gpu: if torch.cuda.is_available(): return torch.device("cuda") - elif hasattr(torch.backends, "mps") and torch.backends.mps.is_available(): + elif ( + hasattr(torch.backends, "mps") + and torch.backends.mps.is_available() + ): return torch.device("mps") # Apple Silicon GPU elif hasattr(torch, "xpu") and torch.xpu.is_available(): # Intel GPU return torch.device("xpu") @@ -41,7 +56,46 @@ def get_device(prefer_gpu=True): device = get_device() -def plot_learning_curve(train_losses, val_losses, train_accs, val_accs, save_path): +def resolve_data_file(csv_file: str) -> Path: + """解析训练数据路径,支持相对路径和常见回退路径。""" + requested = Path(csv_file) + + candidates = [] + # 优先:用户显式传入的路径(绝对或当前工作目录相对) + if requested.is_absolute(): + candidates.append(requested) + else: + candidates.append(requested) + # 再尝试以项目根目录为基准 + candidates.append(PROJECT_ROOT / requested) + + # 默认回退候选(当前仓库标准位置) + candidates.append(PROJECT_ROOT / "data" / "train" / "arknights.csv") + + for path in candidates: + if path.exists() and path.is_file(): + resolved = path.resolve() + if Path(csv_file) != resolved: + print(f"训练数据路径已解析为: {resolved}") + return resolved + + unique_candidates = [] + seen = set() + for path in candidates: + text = str(path) + if text not in seen: + seen.add(text) + unique_candidates.append(text) + + raise FileNotFoundError( + "未找到训练数据文件,请检查路径。已尝试以下位置:\n" + + "\n".join(f"- {p}" for p in unique_candidates) + ) + + +def plot_learning_curve( + train_losses, val_losses, train_accs, val_accs, save_path +): """绘制学习曲线并保存为图片""" epochs = range(1, len(train_losses) + 1) @@ -49,21 +103,21 @@ def plot_learning_curve(train_losses, val_losses, train_accs, val_accs, save_pat # 绘制 Loss 曲线 plt.subplot(1, 2, 1) - plt.plot(epochs, train_losses, 'b-', label='Train Loss') - plt.plot(epochs, val_losses, 'r-', label='Val Loss') - plt.title('Training and Validation Loss') - plt.xlabel('Epochs') - plt.ylabel('Loss') + plt.plot(epochs, train_losses, "b-", label="Train Loss") + plt.plot(epochs, val_losses, "r-", label="Val Loss") + plt.title("Training and Validation Loss") + plt.xlabel("Epochs") + plt.ylabel("Loss") plt.legend() plt.grid(True) # 绘制 Accuracy 曲线 plt.subplot(1, 2, 2) - plt.plot(epochs, train_accs, 'b-', label='Train Acc') - plt.plot(epochs, val_accs, 'r-', label='Val Acc') - plt.title('Training and Validation Accuracy') - plt.xlabel('Epochs') - plt.ylabel('Accuracy (%)') + plt.plot(epochs, train_accs, "b-", label="Train Acc") + plt.plot(epochs, val_accs, "r-", label="Val Acc") + plt.title("Training and Validation Accuracy") + plt.xlabel("Epochs") + plt.ylabel("Accuracy (%)") plt.legend() plt.grid(True) @@ -75,21 +129,27 @@ def plot_learning_curve(train_losses, val_losses, train_accs, val_accs, save_pat def preprocess_data(csv_file): """预处理CSV文件,将异常值修正为合理范围""" - print(f"预处理数据文件: {csv_file}") + csv_path = resolve_data_file(csv_file) + print(f"预处理数据文件: {csv_path}") # 读取CSV文件 - data = pd.read_csv(csv_file, header=None, skiprows=1) + data = pd.read_csv(csv_path, header=None, skiprows=1) print(f"原始数据形状: {data.shape}") # 检查数据形状 expected_columns = TOTAL_FEATURE_COUNT + 2 # +2 for Result and ImgPath if data.shape[1] != expected_columns: - print(f"数据列数不符!期望 {expected_columns} 列,实际 {data.shape[1]} 列") print( - f"期望格式: {MONSTER_COUNT}(怪物L) + {FIELD_FEATURE_COUNT}(场地L) + {MONSTER_COUNT}(怪物R) + {FIELD_FEATURE_COUNT}(场地R) + 1(Result) + 1(ImgPath)") + f"数据列数不符!期望 {expected_columns} 列,实际 {data.shape[1]} 列" + ) + print( + f"期望格式: {MONSTER_COUNT}(怪物L) + {FIELD_FEATURE_COUNT}(场地L) + {MONSTER_COUNT}(怪物R) + {FIELD_FEATURE_COUNT}(场地R) + 1(Result) + 1(ImgPath)" + ) raise Exception("数据格式不符") - data = data.iloc[:, 0: TOTAL_FEATURE_COUNT + 1] # 保留特征和结果列,去掉ImgPath + data = data.iloc[ + :, 0 : TOTAL_FEATURE_COUNT + 1 + ] # 保留特征和结果列,去掉ImgPath # 检查特征范围 features = data.iloc[:, :-1] @@ -119,23 +179,35 @@ def preprocess_data(csv_file): class ArknightsDataset(Dataset): def __init__(self, csv_file, max_value=None): - data = pd.read_csv(csv_file, header=None, skiprows=1) + csv_path = resolve_data_file(csv_file) + data = pd.read_csv(csv_path, header=None, skiprows=1) # 检查数据形状 expected_columns = TOTAL_FEATURE_COUNT + 2 # +2 for Result and ImgPath if data.shape[1] != expected_columns: - print(f"数据列数不符!期望 {expected_columns} 列,实际 {data.shape[1]} 列") + print( + f"数据列数不符!期望 {expected_columns} 列,实际 {data.shape[1]} 列" + ) raise Exception("数据格式不符") - data = data.iloc[:, 0: TOTAL_FEATURE_COUNT + 1] # 保留特征和结果列,去掉ImgPath + data = data.iloc[ + :, 0 : TOTAL_FEATURE_COUNT + 1 + ] # 保留特征和结果列,去掉ImgPath features = data.iloc[:, :-1].values.astype(np.float32) labels = data.iloc[:, -1].map({"L": 0, "R": 1}).values - labels = np.where((labels != 0) & (labels != 1), 0, labels).astype(np.float32) + labels = np.where((labels != 0) & (labels != 1), 0, labels).astype( + np.float32 + ) # 分割双方单位和场地特征 # 数据格式: [怪物L(77), 场地L(6), 怪物R(77), 场地R(6)] left_monster_end = MONSTER_COUNT left_field_end = MONSTER_COUNT + FIELD_FEATURE_COUNT right_monster_end = MONSTER_COUNT + FIELD_FEATURE_COUNT + MONSTER_COUNT - right_field_end = MONSTER_COUNT + FIELD_FEATURE_COUNT + MONSTER_COUNT + FIELD_FEATURE_COUNT + right_field_end = ( + MONSTER_COUNT + + FIELD_FEATURE_COUNT + + MONSTER_COUNT + + FIELD_FEATURE_COUNT + ) # 提取各部分特征 left_monster_features = features[:, :left_monster_end] @@ -144,10 +216,26 @@ def __init__(self, csv_file, max_value=None): right_field_features = features[:, right_monster_end:right_field_end] # 合并怪物特征和场地特征(场地特征直接使用,不取绝对值和符号) - left_counts = np.concatenate([np.abs(left_monster_features), left_field_features], axis=1) - right_counts = np.concatenate([np.abs(right_monster_features), right_field_features], axis=1) - left_signs = np.concatenate([np.sign(left_monster_features), np.ones_like(left_field_features)], axis=1) - right_signs = np.concatenate([np.sign(right_monster_features), np.ones_like(right_field_features)], axis=1) + left_counts = np.concatenate( + [np.abs(left_monster_features), left_field_features], axis=1 + ) + right_counts = np.concatenate( + [np.abs(right_monster_features), right_field_features], axis=1 + ) + left_signs = np.concatenate( + [ + np.sign(left_monster_features), + np.ones_like(left_field_features), + ], + axis=1, + ) + right_signs = np.concatenate( + [ + np.sign(right_monster_features), + np.ones_like(right_field_features), + ], + axis=1, + ) if max_value is not None: left_counts = np.clip(left_counts, 0, max_value) @@ -173,7 +261,9 @@ def __getitem__(self, idx): ) -def train_one_epoch(model, train_loader, criterion, muon_opt, lion_opt, scaler=None): +def train_one_epoch( + model, train_loader, criterion, muon_opt, lion_opt, scaler=None +): model.train() total_loss = 0 correct = 0 @@ -190,19 +280,19 @@ def train_one_epoch(model, train_loader, criterion, muon_opt, lion_opt, scaler=N # 检查输入数据 if ( - torch.isnan(ls).any() - or torch.isnan(lc).any() - or torch.isnan(rs).any() - or torch.isnan(rc).any() + torch.isnan(ls).any() + or torch.isnan(lc).any() + or torch.isnan(rs).any() + or torch.isnan(rc).any() ): print("警告: 输入数据包含NaN,跳过该批次") continue if ( - torch.isinf(ls).any() - or torch.isinf(lc).any() - or torch.isinf(rs).any() - or torch.isinf(rc).any() + torch.isinf(ls).any() + or torch.isinf(lc).any() + or torch.isinf(rs).any() + or torch.isinf(rc).any() ): print("警告: 输入数据包含Inf,跳过该批次") continue @@ -214,7 +304,7 @@ def train_one_epoch(model, train_loader, criterion, muon_opt, lion_opt, scaler=N try: with torch.amp.autocast_mode.autocast( - device_type=device.type, enabled=(scaler is not None) + device_type=device.type, enabled=(scaler is not None) ): outputs = model(ls, lc, rs, rc).squeeze() # 确保输出在合理范围内 @@ -241,14 +331,18 @@ def train_one_epoch(model, train_loader, criterion, muon_opt, lion_opt, scaler=N scaler.unscale_(muon_opt) scaler.unscale_(lion_opt) # 梯度裁剪,避免梯度爆炸 - torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0) + torch.nn.utils.clip_grad_norm_( + model.parameters(), max_norm=1.0 + ) scaler.step(muon_opt) scaler.step(lion_opt) scaler.update() else: # 不使用混合精度 loss.backward() # 梯度裁剪,避免梯度爆炸 - torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0) + torch.nn.utils.clip_grad_norm_( + model.parameters(), max_norm=1.0 + ) muon_opt.step() lion_opt.step() @@ -261,7 +355,9 @@ def train_one_epoch(model, train_loader, criterion, muon_opt, lion_opt, scaler=N print(f"警告: 训练过程中出错 - {str(e)}") continue - return total_loss / max(1, len(train_loader)), 100 * correct / max(1, total) + return total_loss / max(1, len(train_loader)), 100 * correct / max( + 1, total + ) def evaluate(model, data_loader, criterion): @@ -273,19 +369,20 @@ def evaluate(model, data_loader, criterion): with torch.no_grad(): for ls, lc, rs, rc, labels in data_loader: ls, lc, rs, rc, labels = [ - x.to(device, non_blocking=True) for x in (ls, lc, rs, rc, labels) + x.to(device, non_blocking=True) + for x in (ls, lc, rs, rc, labels) ] # 检查输入值范围 if ( - torch.isnan(ls).any() - or torch.isnan(lc).any() - or torch.isnan(rs).any() - or torch.isnan(rc).any() - or torch.isinf(ls).any() - or torch.isinf(lc).any() - or torch.isinf(rs).any() - or torch.isinf(rc).any() + torch.isnan(ls).any() + or torch.isnan(lc).any() + or torch.isnan(rs).any() + or torch.isnan(rc).any() + or torch.isinf(ls).any() + or torch.isinf(lc).any() + or torch.isinf(rs).any() + or torch.isinf(rc).any() ): print("警告: 评估时输入数据包含NaN或Inf,跳过该批次") continue @@ -296,11 +393,14 @@ def evaluate(model, data_loader, criterion): try: with torch.amp.autocast_mode.autocast( - device_type=device.type, enabled=(device.type == "cuda") + device_type=device.type, enabled=(device.type == "cuda") ): outputs = model(ls, lc, rs, rc).squeeze() # 确保输出在合理范围内 - if torch.isnan(outputs).any() or torch.isinf(outputs).any(): + if ( + torch.isnan(outputs).any() + or torch.isinf(outputs).any() + ): print("警告: 评估时模型输出包含NaN或Inf,跳过该批次") continue # 确保输出严格在0-1之间 @@ -345,7 +445,7 @@ def stratified_random_split(dataset, test_size=0.1, seed=42): def main(): # 配置参数 config = { - "data_file": "arknights.csv", + "data_file": str(PROJECT_ROOT / "data" / "train" / "arknights.csv"), "batch_size": 1024, "test_size": 0.1, "embed_dim": 256, @@ -356,9 +456,11 @@ def main(): "lion_lr": 3e-4 / 10, # 论文指出 Lion 优化器需要更小的学习率 "epochs": 50, "seed": 42, # 随机数种子 - "save_dir": "models", # 存到哪里 + "save_dir": PROJECT_ROOT / "models", # 存到哪里 "max_feature_value": 100, # 限制特征最大值,防止极端值造成不稳定 - "num_workers": 0 if torch.cuda.is_available() else 0, # 根据CUDA可用性设置num_workers + "num_workers": ( + 0 if torch.cuda.is_available() else 0 + ), # 根据CUDA可用性设置num_workers } # 创建保存目录 @@ -422,7 +524,9 @@ def main(): num_workers=config["num_workers"], ) val_loader = DataLoader( - val_dataset, batch_size=config["batch_size"], num_workers=config["num_workers"] + val_dataset, + batch_size=config["batch_size"], + num_workers=config["num_workers"], ) # 初始化模型 @@ -436,16 +540,27 @@ def main(): dropout=config["dropout"], # 传入 dropout ).to(device) - print(f"模型使用特征数: 怪物({MONSTER_COUNT}) + 场地({FIELD_FEATURE_COUNT}) = {total_units}") - print(f"模型参数数量: {sum(p.numel() for p in model.parameters() if p.requires_grad)}") + print( + f"模型使用特征数: 怪物({MONSTER_COUNT}) + 场地({FIELD_FEATURE_COUNT}) = {total_units}" + ) + print( + f"模型参数数量: {sum(p.numel() for p in model.parameters() if p.requires_grad)}" + ) # 损失函数和优化器 (引入 Muon 与 Lion) criterion = nn.MSELoss() muon_opt, lion_opt = get_muon_lion_optimizers( - model, muon_lr=config["lr"], lion_lr=config["lion_lr"], weight_decay=1e-1 + model, + muon_lr=config["lr"], + lion_lr=config["lion_lr"], + weight_decay=1e-1, + ) + scheduler_muon = optim.lr_scheduler.CosineAnnealingLR( + muon_opt, T_max=config["epochs"] + ) + scheduler_lion = optim.lr_scheduler.CosineAnnealingLR( + lion_opt, T_max=config["epochs"] ) - scheduler_muon = optim.lr_scheduler.CosineAnnealingLR(muon_opt, T_max=config["epochs"]) - scheduler_lion = optim.lr_scheduler.CosineAnnealingLR(lion_opt, T_max=config["epochs"]) # 训练历史记录 train_losses, val_losses, train_accs, val_accs = [], [], [], [] @@ -501,7 +616,9 @@ def main(): # }, os.path.join(config['save_dir'], 'latest_checkpoint.pth')) # 打印训练信息 - print(f"Train Loss: {train_loss:.4f} | Acc: {train_acc:.2f}% Val Loss: {val_loss:.4f} | Acc: {val_acc:.2f}%") + print( + f"Train Loss: {train_loss:.4f} | Acc: {train_acc:.2f}% Val Loss: {val_loss:.4f} | Acc: {val_acc:.2f}%" + ) # 计时 if epoch == 0: @@ -513,7 +630,9 @@ def main(): elapsed_time = current_time - start_time avg_epoch_time = elapsed_time / (epoch + 1) remaining_time = (avg_epoch_time * config["epochs"]) - elapsed_time - print(f"Epoch Time: {epoch_duration:.2f}s, Estimated Remaining: {remaining_time / 60:.2f}min") + print( + f"Epoch Time: {epoch_duration:.2f}s, Estimated Remaining: {remaining_time / 60:.2f}min" + ) epoch_start_time = current_time # Reset for next epoch print("-" * 40) @@ -526,10 +645,17 @@ def main(): for model_type in ["acc", "loss", "full"]: old_path = save_dir_path / f"best_model_{model_type}.pth" if old_path.exists(): - old_path.rename(save_dir_path / f"best_model_{model_type}_{base_filename}") - - plot_learning_curve(train_losses, val_losses, train_accs, val_accs, - save_dir_path / f"learning_curve_{base_filename}.png") + old_path.rename( + save_dir_path / f"best_model_{model_type}_{base_filename}" + ) + + plot_learning_curve( + train_losses, + val_losses, + train_accs, + val_accs, + save_dir_path / f"learning_curve_{base_filename}.png", + ) if __name__ == "__main__": diff --git a/val.py b/src/models/val.py similarity index 71% rename from val.py rename to src/models/val.py index 2d83354..f91a52f 100644 --- a/val.py +++ b/src/models/val.py @@ -1,7 +1,8 @@ import torch import torch.nn as nn from torch.utils.data import DataLoader -from train import UnitAwareTransformer,ArknightsDataset +from src.models.train import UnitAwareTransformer, ArknightsDataset + def evaluate(model, data_loader, criterion, device): model.eval() @@ -16,8 +17,16 @@ def evaluate(model, data_loader, criterion, device): # 其余代码保持不变... # 检查输入值范围 - if torch.isnan(ls).any() or torch.isnan(lc).any() or torch.isnan(rs).any() or torch.isnan(rc).any() or \ - torch.isinf(ls).any() or torch.isinf(lc).any() or torch.isinf(rs).any() or torch.isinf(rc).any(): + if ( + torch.isnan(ls).any() + or torch.isnan(lc).any() + or torch.isnan(rs).any() + or torch.isnan(rc).any() + or torch.isinf(ls).any() + or torch.isinf(lc).any() + or torch.isinf(rs).any() + or torch.isinf(rc).any() + ): print("警告: 评估时输入数据包含NaN或Inf,跳过该批次") continue @@ -54,14 +63,15 @@ def evaluate(model, data_loader, criterion, device): return total_loss / max(1, len(data_loader)), 100 * correct / max(1, total) + def main(): config = { - 'data_file': 'arknights_clean.csv', - 'batch_size': 256, - 'max_feature_value': 200 # 限制特征最大值,防止极端值造成不稳定 + "data_file": "arknights_clean.csv", + "batch_size": 256, + "max_feature_value": 200, # 限制特征最大值,防止极端值造成不稳定 } # 设置设备 - device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') + device = torch.device("cuda" if torch.cuda.is_available() else "cpu") print(f"使用设备: {device}") # 检查CUDA可用性 @@ -75,36 +85,41 @@ def main(): torch.backends.cudnn.benchmark = False else: print("警告: 未检测到GPU,将在CPU上运行训练,这可能会很慢!") - + dataset = ArknightsDataset( - config['data_file'], - max_value=config['max_feature_value'] # 使用最大值限制 + config["data_file"], + max_value=config["max_feature_value"], # 使用最大值限制 ) - + # 修改这里 - 只有当数据在CPU上时才使用pin_memory val_loader = DataLoader( - dataset, - batch_size=config['batch_size'], + dataset, + batch_size=config["batch_size"], num_workers=0, - pin_memory=False # 禁用pin_memory,或者根据你的数据集情况调整 + pin_memory=False, # 禁用pin_memory,或者根据你的数据集情况调整 ) - + model = UnitAwareTransformer( num_units=34, # 更新为34个怪物 embed_dim=128, num_heads=8, - num_layers=4 # 注意:train.py中config['n_layers']=4 + num_layers=4, # 注意:train.py中config['n_layers']=4 ).to(device) # 加载模型权重 try: - model = torch.load('models/best_model_full.pth', map_location=device, weights_only=False) + model = torch.load( + "models/best_model_full.pth", + map_location=device, + weights_only=False, + ) except TypeError: # 如果旧版本 PyTorch 不认识 weights_only - model = torch.load('models/best_model_full.pth', map_location=device) + model = torch.load("models/best_model_full.pth", map_location=device) model.eval() criterion = nn.BCELoss() val_loss, val_acc = evaluate(model, val_loader, criterion, device) print(f"Val Loss: {val_loss:.4f} | Acc: {val_acc:.2f}%") - + + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/src/recognition/__init__.py b/src/recognition/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/field_recognition.py b/src/recognition/field_recognition.py similarity index 68% rename from field_recognition.py rename to src/recognition/field_recognition.py index 59344bb..c179e1c 100644 --- a/field_recognition.py +++ b/src/recognition/field_recognition.py @@ -6,7 +6,6 @@ from collections import defaultdict from PIL import Image import cv2 -import numpy as np import torch import torch.nn as nn from torchvision import models, transforms @@ -18,22 +17,22 @@ "altar_vertical": [ {"x": 910, "y": 174, "width": 95, "height": 104}, {"x": 910, "y": 429, "width": 102, "height": 108}, - {"x": 900, "y": 755, "width": 120, "height": 108} + {"x": 900, "y": 755, "width": 120, "height": 108}, ], "block_parallel": [ {"x": 694, "y": 240, "width": 530, "height": 122}, - {"x": 651, "y": 614, "width": 620, "height": 143} + {"x": 651, "y": 614, "width": 620, "height": 143}, ], "block_vertical": [ {"x": 647, "y": 233, "width": 153, "height": 523}, - {"x": 1112, "y": 239, "width": 159, "height": 514} + {"x": 1112, "y": 239, "width": 159, "height": 514}, ], "coil_narrow": [ {"x": 915, "y": 110, "width": 85, "height": 89}, {"x": 815, "y": 257, "width": 86, "height": 98}, {"x": 1024, "y": 258, "width": 79, "height": 98}, {"x": 790, "y": 643, "width": 97, "height": 102}, - {"x": 1031, "y": 639, "width": 102, "height": 108} + {"x": 1031, "y": 639, "width": 102, "height": 108}, ], "coil_wide": [ {"x": 719, "y": 181, "width": 81, "height": 89}, @@ -43,23 +42,18 @@ {"x": 1159, "y": 757, "width": 93, "height": 92}, {"x": 1257, "y": 533, "width": 94, "height": 102}, {"x": 1236, "y": 344, "width": 85, "height": 97}, - {"x": 1120, "y": 180, "width": 75, "height": 91} - ], - "crossbow_top": [ - {"x": 718, "y": 13, "width": 484, "height": 106} - ], - "fire_side_left": [ - {"x": 98, "y": 246, "width": 184, "height": 281} - ], - "fire_side_right": [ - {"x": 1656, "y": 430, "width": 235, "height": 315} + {"x": 1120, "y": 180, "width": 75, "height": 91}, ], + "crossbow_top": [{"x": 718, "y": 13, "width": 484, "height": 106}], + "fire_side_left": [{"x": 98, "y": 246, "width": 184, "height": 281}], + "fire_side_right": [{"x": 1656, "y": 430, "width": 235, "height": 315}], "fire_top": [ {"x": 532, "y": 17, "width": 188, "height": 97}, - {"x": 1325, "y": 14, "width": 60, "height": 100} - ] + {"x": 1325, "y": 14, "width": 60, "height": 100}, + ], } + class FieldRecognizer: def __init__(self): self.field_model = None @@ -69,7 +63,7 @@ def __init__(self): self.grouped_elements = {} self.image_feature_columns = [] self.is_initialized = False - + # 初始化场地识别模型 self._init_field_recognition() @@ -77,19 +71,23 @@ def _init_field_recognition(self): """初始化场地识别模型和相关组件""" try: # 设置设备 - self.field_device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + self.field_device = torch.device( + "cuda" if torch.cuda.is_available() else "cpu" + ) logger.info(f"场地识别将使用设备: {self.field_device}") - + # 加载类别映射 model_dir = Path("tools/battlefield_recognize") class_map_path = model_dir / "class_to_idx.json" pth_model_path = model_dir / "field_recognize.pth" - + if not class_map_path.exists(): - logger.warning("找不到场地识别类别映射文件,跳过场地识别初始化") + logger.warning( + "找不到场地识别类别映射文件,跳过场地识别初始化" + ) return - with open(class_map_path, 'r', encoding='utf-8') as f: + with open(class_map_path, "r", encoding="utf-8") as f: class_to_idx = json.load(f) self.idx_to_class = {v: k for k, v in class_to_idx.items()} num_classes = len(class_to_idx) @@ -97,35 +95,45 @@ def _init_field_recognition(self): # 准备特征列 self.grouped_elements = defaultdict(list) for class_name in class_to_idx.keys(): - if class_name.endswith('_none'): + if class_name.endswith("_none"): continue - condensed_name = re.sub(r'_left_', '_', class_name) - condensed_name = re.sub(r'_right_', '_', condensed_name) + condensed_name = re.sub(r"_left_", "_", class_name) + condensed_name = re.sub(r"_right_", "_", condensed_name) self.grouped_elements[condensed_name].append(class_name) self.image_feature_columns = sorted(self.grouped_elements.keys()) - + if not pth_model_path.exists(): logger.warning("找不到场地识别模型文件,跳过场地识别初始化") return # 加载模型 - self.field_model = self._load_pytorch_model(str(pth_model_path), num_classes, self.field_device) - + self.field_model = self._load_pytorch_model( + str(pth_model_path), num_classes, self.field_device + ) + # 设置图像变换 - self.field_transform = transforms.Compose([ - transforms.Resize((224, 224)), - transforms.ToTensor(), - transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) - ]) - + self.field_transform = transforms.Compose( + [ + transforms.Resize((224, 224)), + transforms.ToTensor(), + transforms.Normalize( + [0.485, 0.456, 0.406], [0.229, 0.224, 0.225] + ), + ] + ) + self.is_initialized = True - logger.info(f"场地识别初始化成功,将生成 {len(self.image_feature_columns)} 个特征列") - + logger.info( + f"场地识别初始化成功,将生成 {len(self.image_feature_columns)} 个特征列" + ) + except Exception as e: logger.error(f"场地识别初始化失败: {e}") self.is_initialized = False - def _load_pytorch_model(self, model_path: str, num_classes: int, device: torch.device): + def _load_pytorch_model( + self, model_path: str, num_classes: int, device: torch.device + ): """加载 PyTorch 模型并设置为评估模式""" logger.info(f"正在加载 PyTorch 模型: {model_path}") model = models.mobilenet_v3_small(weights=None) @@ -137,10 +145,12 @@ def _load_pytorch_model(self, model_path: str, num_classes: int, device: torch.d logger.info("模型加载成功并已切换到评估模式。") return model - def _predict_scene_pytorch(self, image_path: str, threshold: float = 0.5) -> list[str]: + def _predict_scene_pytorch( + self, image_path: str, threshold: float = 0.5 + ) -> list[str]: """使用 PyTorch 模型对给定图片的所有 ROI 进行分类预测""" try: - full_image = Image.open(image_path).convert('RGB') + full_image = Image.open(image_path).convert("RGB") except Exception: return [] @@ -151,18 +161,27 @@ def _predict_scene_pytorch(self, image_path: str, threshold: float = 0.5) -> lis with torch.no_grad(): for location, boxes in ROI_COORDINATES.items(): for i, box in enumerate(boxes): - x, y, w, h = box['x'], box['y'], box['width'], box['height'] + x, y, w, h = ( + box["x"], + box["y"], + box["width"], + box["height"], + ) roi_pil = full_image.crop((x, y, x + w, y + h)) input_tensor = self.field_transform(roi_pil).unsqueeze(0) input_tensor = input_tensor.to(self.field_device) outputs = self.field_model(input_tensor) - probabilities = torch.nn.functional.softmax(outputs, dim=1)[0] - max_prob, predicted_index_tensor = torch.max(probabilities, 0) + probabilities = torch.nn.functional.softmax( + outputs, dim=1 + )[0] + max_prob, predicted_index_tensor = torch.max( + probabilities, 0 + ) predicted_index = predicted_index_tensor.item() if max_prob.item() >= threshold: predicted_class = self.idx_to_class[predicted_index] - if not predicted_class.endswith('_none'): + if not predicted_class.endswith("_none"): detected_classes.append(predicted_class) return detected_classes @@ -171,31 +190,37 @@ def recognize_field_elements(self, screenshot): if not self.is_initialized or self.field_model is None: logger.debug("场地识别模型未初始化,跳过场地识别") return {} - + try: # 将OpenCV图像转换为PIL图像 screenshot_rgb = cv2.cvtColor(screenshot, cv2.COLOR_BGR2RGB) pil_image = Image.fromarray(screenshot_rgb) - + # 保存临时文件用于识别 temp_image_path = "temp_screenshot.png" pil_image.save(temp_image_path) - + # 进行场地识别 - detected_full_names = set(self._predict_scene_pytorch(temp_image_path, threshold=0.5)) - + detected_full_names = set( + self._predict_scene_pytorch(temp_image_path, threshold=0.5) + ) + # 删除临时文件 if os.path.exists(temp_image_path): os.remove(temp_image_path) - + # 处理识别结果 field_data = {} for condensed_name, full_names in self.grouped_elements.items(): num_positions = len(full_names) if num_positions == 1: - field_data[condensed_name] = 1 if full_names[0] in detected_full_names else 0 + field_data[condensed_name] = ( + 1 if full_names[0] in detected_full_names else 0 + ) else: - detections_in_group = [fn in detected_full_names for fn in full_names] + detections_in_group = [ + fn in detected_full_names for fn in full_names + ] num_detected = sum(detections_in_group) if num_detected == num_positions: field_data[condensed_name] = 1 @@ -203,10 +228,12 @@ def recognize_field_elements(self, screenshot): field_data[condensed_name] = 0 else: field_data[condensed_name] = -1 - - logger.debug(f"场地识别完成,检测到元素: {list(detected_full_names)}") + + logger.debug( + f"场地识别完成,检测到元素: {list(detected_full_names)}" + ) return field_data - + except Exception as e: logger.error(f"场地识别失败: {e}") return {} diff --git a/find_monster_zone.py b/src/recognition/find_monster_zone.py similarity index 58% rename from find_monster_zone.py rename to src/recognition/find_monster_zone.py index 8d3b273..b837bba 100644 --- a/find_monster_zone.py +++ b/src/recognition/find_monster_zone.py @@ -2,9 +2,12 @@ import cv2 import numpy as np +from src.core.paths import ensure_tmp_images_dir + logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) + def custom_least_squares(fun, x0, args=()): """ A simple linear least squares solver to replace scipy.optimize.least_squares. @@ -13,10 +16,13 @@ def custom_least_squares(fun, x0, args=()): x = np.array(x0, dtype=float) r = np.array(fun(x, *args)) if r.size == 0: + class Result: - def __init__(self, x): self.x = x + def __init__(self, x): + self.x = x + return Result(x) - + n = len(x) J = np.zeros((len(r), n)) eps = 1e-6 @@ -25,13 +31,16 @@ def __init__(self, x): self.x = x x_eps[i] += eps r_eps = np.array(fun(x_eps, *args)) J[:, i] = (r_eps - r) / eps - + delta, _, _, _ = np.linalg.lstsq(J, -r, rcond=None) - + class Result: - def __init__(self, x): self.x = x + def __init__(self, x): + self.x = x + return Result(x + delta) + # 类伽马变换函数 def adjust_quasi_gamma(image): c = np.arange(256.0 / 255, step=1.0 / 255) @@ -130,7 +139,9 @@ def find_big(crop, x_ratio, minR, maxR, width, p1=30, p2=35): results.append(j) logger.debug(f"big circle: {j}") else: - logger.warning(f"section{x_ratio.index(i)} big circle not detected") + logger.warning( + f"section{x_ratio.index(i)} big circle not detected" + ) results = np.array(results) return results @@ -151,14 +162,18 @@ def find_small(crop_small, x_ratio_small, x_ratio, minR, maxR, width): ) if circles_small is not None: circles_small_bias = circles_small[0].copy() - circles_small_bias[:, 0] = circles_small[0][:, 0] + np.round(i * width) + circles_small_bias[:, 0] = circles_small[0][:, 0] + np.round( + i * width + ) for m in circles_small_bias: j = np.append(m, x_ratio.index(i)) results.append(j) logger.debug(f"small circle: {j}") else: - logger.warning(f"section{x_ratio.index(i)} small circle not detected") + logger.warning( + f"section{x_ratio.index(i)} small circle not detected" + ) results = np.array(results) return results @@ -187,7 +202,9 @@ def detect_outliers(coords, threshold=0.1): std_avg_distance = np.std(avg_distances) # 找出距离均值超过阈值倍标准差的点 - outliers = np.where(avg_distances > mean_avg_distance + threshold * std_avg_distance)[0] + outliers = np.where( + avg_distances > mean_avg_distance + threshold * std_avg_distance + )[0] # 剔除异常点 filtered_coords = np.delete(coords, outliers, axis=0) @@ -197,46 +214,191 @@ def detect_outliers(coords, threshold=0.1): # 框架创建 def create_frame(cx, cy, r, high_tol=False): - k = 1.0401189 #修正因子 - m = 4.7102526 #中部padding - nums_bias=0.9 - nums_bias_inn=0.3 - nums_y=cy-0.5*r - - if high_tol==False: - t = 0.025 #容差因子 - avatar = np.round(np.array([[cx -r*t, cy +r*t, cx+2*k*r+r*t, cy-2*k*r-r*t], - [cx+2*k*r-r*t, cy-2*k*r-r*t, cx+4*k*r+r*t, cy +r*t], - [cx+4*k*r-r*t, cy +r*t, cx+6*k*r+r*t, cy-2*k*r-r*t], - [cx+(6 *k+m)*r-r*t, cy +r*t, cx+(8 *k+m)*r+r*t, cy-2*k*r-r*t], - [cx+(8 *k+m)*r-r*t, cy-2*k*r-r*t, cx+(10*k+m)*r+r*t, cy +r*t], - [cx+(10*k+m)*r-r*t, cy +r*t, cx+(12*k+m)*r+r*t, cy-2*k*r-r*t]])).astype("int") - nums = np.round(np.array([[cx+(nums_bias+0*k)*r-r*t, cy+r*t, cx+(nums_bias_inn+2*k)*r+r*t, nums_y], - [cx+(nums_bias+2*k)*r-r*t, nums_y, cx+(nums_bias_inn+4*k)*r+r*t, cy+r*t], - [cx+(nums_bias+4*k)*r-r*t, cy+r*t, cx+(nums_bias_inn+6*k)*r+r*t, nums_y], - [cx+(-nums_bias_inn+6 *k+m)*r-r*t, cy+r*t, cx+(-nums_bias+8 *k+m)*r+r*t, nums_y], - [cx+(-nums_bias_inn+8 *k+m)*r-r*t, nums_y, cx+(-nums_bias+10*k+m)*r+r*t, cy+r*t], - [cx+(-nums_bias_inn+10*k+m)*r-r*t, cy+r*t, cx+(-nums_bias+12*k+m)*r+r*t, nums_y]])).astype("int") - - return avatar,nums - - if high_tol==True: - t = 0.17 #容差因子 - avatar = np.round(np.array([[cx -r*t, cy +r*t, cx+2*k*r+r*t, cy-2*k*r-r*t], - [cx+2*k*r-r*t, cy-2*k*r-r*t, cx+4*k*r+r*t, cy +r*t], - [cx+4*k*r-r*t, cy +r*t, cx+6*k*r+r*t, cy-2*k*r-r*t], - [cx+(6 *k+m)*r-r*t, cy +r*t, cx+(8 *k+m)*r+r*t, cy-2*k*r-r*t], - [cx+(8 *k+m)*r-r*t, cy-2*k*r-r*t, cx+(10*k+m)*r+r*t, cy +r*t], - [cx+(10*k+m)*r-r*t, cy +r*t, cx+(12*k+m)*r+r*t, cy-2*k*r-r*t]])).astype("int") - nums = np.round(np.array([[cx+(nums_bias+0*k)*r-r*t, cy+r*t, cx+(nums_bias_inn+2*k)*r+r*t, nums_y], - [cx+(nums_bias+2*k)*r-r*t, nums_y, cx+(nums_bias_inn+4*k)*r+r*t, cy+r*t], - [cx+(nums_bias+4*k)*r-r*t, cy+r*t, cx+(nums_bias_inn+6*k)*r+r*t, nums_y], - [cx+(-nums_bias_inn+6 *k+m)*r-r*t, cy+r*t, cx+(-nums_bias+8 *k+m)*r+r*t, nums_y], - [cx+(-nums_bias_inn+8 *k+m)*r-r*t, nums_y, cx+(-nums_bias+10*k+m)*r+r*t, cy+r*t], - [cx+(-nums_bias_inn+10*k+m)*r-r*t, cy+r*t, cx+(-nums_bias+12*k+m)*r+r*t, nums_y]])).astype("int") - - return avatar,nums - + k = 1.0401189 # 修正因子 + m = 4.7102526 # 中部padding + nums_bias = 0.9 + nums_bias_inn = 0.3 + nums_y = cy - 0.5 * r + + if high_tol == False: + t = 0.025 # 容差因子 + avatar = np.round( + np.array( + [ + [ + cx - r * t, + cy + r * t, + cx + 2 * k * r + r * t, + cy - 2 * k * r - r * t, + ], + [ + cx + 2 * k * r - r * t, + cy - 2 * k * r - r * t, + cx + 4 * k * r + r * t, + cy + r * t, + ], + [ + cx + 4 * k * r - r * t, + cy + r * t, + cx + 6 * k * r + r * t, + cy - 2 * k * r - r * t, + ], + [ + cx + (6 * k + m) * r - r * t, + cy + r * t, + cx + (8 * k + m) * r + r * t, + cy - 2 * k * r - r * t, + ], + [ + cx + (8 * k + m) * r - r * t, + cy - 2 * k * r - r * t, + cx + (10 * k + m) * r + r * t, + cy + r * t, + ], + [ + cx + (10 * k + m) * r - r * t, + cy + r * t, + cx + (12 * k + m) * r + r * t, + cy - 2 * k * r - r * t, + ], + ] + ) + ).astype("int") + nums = np.round( + np.array( + [ + [ + cx + (nums_bias + 0 * k) * r - r * t, + cy + r * t, + cx + (nums_bias_inn + 2 * k) * r + r * t, + nums_y, + ], + [ + cx + (nums_bias + 2 * k) * r - r * t, + nums_y, + cx + (nums_bias_inn + 4 * k) * r + r * t, + cy + r * t, + ], + [ + cx + (nums_bias + 4 * k) * r - r * t, + cy + r * t, + cx + (nums_bias_inn + 6 * k) * r + r * t, + nums_y, + ], + [ + cx + (-nums_bias_inn + 6 * k + m) * r - r * t, + cy + r * t, + cx + (-nums_bias + 8 * k + m) * r + r * t, + nums_y, + ], + [ + cx + (-nums_bias_inn + 8 * k + m) * r - r * t, + nums_y, + cx + (-nums_bias + 10 * k + m) * r + r * t, + cy + r * t, + ], + [ + cx + (-nums_bias_inn + 10 * k + m) * r - r * t, + cy + r * t, + cx + (-nums_bias + 12 * k + m) * r + r * t, + nums_y, + ], + ] + ) + ).astype("int") + + return avatar, nums + + if high_tol == True: + t = 0.17 # 容差因子 + avatar = np.round( + np.array( + [ + [ + cx - r * t, + cy + r * t, + cx + 2 * k * r + r * t, + cy - 2 * k * r - r * t, + ], + [ + cx + 2 * k * r - r * t, + cy - 2 * k * r - r * t, + cx + 4 * k * r + r * t, + cy + r * t, + ], + [ + cx + 4 * k * r - r * t, + cy + r * t, + cx + 6 * k * r + r * t, + cy - 2 * k * r - r * t, + ], + [ + cx + (6 * k + m) * r - r * t, + cy + r * t, + cx + (8 * k + m) * r + r * t, + cy - 2 * k * r - r * t, + ], + [ + cx + (8 * k + m) * r - r * t, + cy - 2 * k * r - r * t, + cx + (10 * k + m) * r + r * t, + cy + r * t, + ], + [ + cx + (10 * k + m) * r - r * t, + cy + r * t, + cx + (12 * k + m) * r + r * t, + cy - 2 * k * r - r * t, + ], + ] + ) + ).astype("int") + nums = np.round( + np.array( + [ + [ + cx + (nums_bias + 0 * k) * r - r * t, + cy + r * t, + cx + (nums_bias_inn + 2 * k) * r + r * t, + nums_y, + ], + [ + cx + (nums_bias + 2 * k) * r - r * t, + nums_y, + cx + (nums_bias_inn + 4 * k) * r + r * t, + cy + r * t, + ], + [ + cx + (nums_bias + 4 * k) * r - r * t, + cy + r * t, + cx + (nums_bias_inn + 6 * k) * r + r * t, + nums_y, + ], + [ + cx + (-nums_bias_inn + 6 * k + m) * r - r * t, + cy + r * t, + cx + (-nums_bias + 8 * k + m) * r + r * t, + nums_y, + ], + [ + cx + (-nums_bias_inn + 8 * k + m) * r - r * t, + nums_y, + cx + (-nums_bias + 10 * k + m) * r + r * t, + cy + r * t, + ], + [ + cx + (-nums_bias_inn + 10 * k + m) * r - r * t, + cy + r * t, + cx + (-nums_bias + 12 * k + m) * r + r * t, + nums_y, + ], + ] + ) + ).astype("int") + + return avatar, nums + + def filter(results_big, results_small, height): high_tol = 0 big_key = 0 @@ -274,7 +436,9 @@ def filter(results_big, results_small, height): filtered_big = [] logger.warning("仅使用小圆进入最小二乘") else: - r_refer = np.abs(filtered_small[1, 0] - filtered_small[0, 0]) / 16.39 # 大圆的参考半径 + r_refer = ( + np.abs(filtered_small[1, 0] - filtered_small[0, 0]) / 16.39 + ) # 大圆的参考半径 diff_r = np.abs(results_big[:, 2] - r_refer) filtered_big = results_big[diff_r <= 0.05 * r_refer] # y筛选 @@ -283,7 +447,9 @@ def filter(results_big, results_small, height): # 如果数组为空,报错 if filtered_big.size == 0: logger.error(results_big) - raise IndexError("std_y筛选出现问题,请检查以上数据输入是否合法") + raise IndexError( + "std_y筛选出现问题,请检查以上数据输入是否合法" + ) mean_value = np.mean(filtered_big[:, 1]) abs_diff = np.abs(filtered_big[:, 1] - mean_value) @@ -298,7 +464,9 @@ def filter(results_big, results_small, height): if n in [0, 1, 2]: p_cx.append(x - ((2 * n + 1) * k * r_refer)) elif n in [3, 4, 5]: - p_cx.append(x - ((2 * n + 1) * k * r_refer + 4.710 * r_refer)) + p_cx.append( + x - ((2 * n + 1) * k * r_refer + 4.710 * r_refer) + ) filtered_big_p = np.column_stack((filtered_big, p_cx)) std_x = np.std(filtered_big_p[:, -1]) @@ -306,12 +474,16 @@ def filter(results_big, results_small, height): # 如果数组为空,报错 if filtered_big_p.size == 0: logger.error(results_big) - raise IndexError("std_x筛选出现问题,请检查以上数据输入是否合法") + raise IndexError( + "std_x筛选出现问题,请检查以上数据输入是否合法" + ) mean_value = np.mean(filtered_big_p[:, -1]) abs_diff = np.abs(filtered_big_p[:, -1] - mean_value) outlier_index = np.argmax(abs_diff) - filtered_big_p = np.delete(filtered_big_p, outlier_index, axis=0) + filtered_big_p = np.delete( + filtered_big_p, outlier_index, axis=0 + ) std_x = np.std(filtered_big_p[:, -1]) filtered_big = filtered_big_p[:, :-1] @@ -333,10 +505,14 @@ def filter(results_big, results_small, height): if n in [0, 1, 2]: a_cx = x - ((2 * n + 1) * k * r_refer) elif n in [3, 4, 5]: - a_cx = x - ((2 * n + 1) * k * r_refer + 4.710 * r_refer) + a_cx = x - ( + (2 * n + 1) * k * r_refer + 4.710 * r_refer + ) a_cy = y + radius std.append([a_cx, a_cy]) - _, out_index = detect_outliers(std, threshold=0.02 * np.mean(results_big[:, 2])) + _, out_index = detect_outliers( + std, threshold=0.02 * np.mean(results_big[:, 2]) + ) filtered_big = np.delete(results_big, out_index, axis=0) high_tol = 1 @@ -355,10 +531,14 @@ def filter(results_big, results_small, height): if n in [0, 1, 2]: a_cx = x - ((2 * n + 1) * k * r_refer) elif n in [3, 4, 5]: - a_cx = x - ((2 * n + 1) * k * r_refer + 4.710 * r_refer) + a_cx = x - ( + (2 * n + 1) * k * r_refer + 4.710 * r_refer + ) a_cy = y + radius std.append([a_cx, a_cy]) - _, out_index = detect_outliers(std, threshold=0.02 * np.mean(results_big[:, 2])) + _, out_index = detect_outliers( + std, threshold=0.02 * np.mean(results_big[:, 2]) + ) filtered_big = np.delete(results_big, out_index, axis=0) else: filtered_big = [] @@ -379,25 +559,55 @@ def cutFrame(image, high_tol=False): R_sets = flex_pixel(image) try: - crop_blur, crop_small, x_ratio, x_ratio_small = preprocess(image, blur=11) + crop_blur, crop_small, x_ratio, x_ratio_small = preprocess( + image, blur=11 + ) results_big = find_big( - crop_blur, x_ratio, minR=R_sets[0], maxR=R_sets[1], width=width, p1=21, p2=28 + crop_blur, + x_ratio, + minR=R_sets[0], + maxR=R_sets[1], + width=width, + p1=21, + p2=28, ) results_small = find_small( - crop_small, x_ratio_small, x_ratio, minR=R_sets[2], maxR=R_sets[3], width=width + crop_small, + x_ratio_small, + x_ratio, + minR=R_sets[2], + maxR=R_sets[3], + width=width, + ) + filtered_big, filtered_small, high_tol = filter( + results_big, results_small, height ) - filtered_big, filtered_small, high_tol = filter(results_big, results_small, height) except IndexError: try: - crop_blur, crop_small, x_ratio, x_ratio_small = preprocess(image, blur=7, spare=1) + crop_blur, crop_small, x_ratio, x_ratio_small = preprocess( + image, blur=7, spare=1 + ) results_big = find_big( - crop_blur, x_ratio, minR=R_sets[0], maxR=R_sets[1], width=width, p1=18, p2=24 + crop_blur, + x_ratio, + minR=R_sets[0], + maxR=R_sets[1], + width=width, + p1=18, + p2=24, ) results_small = find_small( - crop_small, x_ratio_small, x_ratio, minR=R_sets[2], maxR=R_sets[3], width=width + crop_small, + x_ratio_small, + x_ratio, + minR=R_sets[2], + maxR=R_sets[3], + width=width, + ) + filtered_big, filtered_small, high_tol = filter( + results_big, results_small, height ) - filtered_big, filtered_small, high_tol = filter(results_big, results_small, height) except IndexError: logger.error("备用参数捕捉失败!请重新框选试试") @@ -434,8 +644,12 @@ def residuals(params, large_circles, small_circles): # 定义目标函数 initial_guess = [0, 200, 60] # 使用最小二乘法求解 - result = custom_least_squares(residuals, initial_guess, args=(filtered_big, filtered_small)) - avatar, nums = create_frame(result.x[0], result.x[1], result.x[2], high_tol) + result = custom_least_squares( + residuals, initial_guess, args=(filtered_big, filtered_small) + ) + avatar, nums = create_frame( + result.x[0], result.x[1], result.x[2], high_tol + ) divisors = np.array([width, height, width, height]) d_avatar = avatar / divisors @@ -445,7 +659,7 @@ def residuals(params, large_circles, small_circles): # 定义目标函数 if __name__ == "__main__": - image = cv2.imread("images/tmp/zone1.png") + image = cv2.imread(str(ensure_tmp_images_dir() / "zone1.png")) height, width, _ = image.shape @@ -453,10 +667,21 @@ def residuals(params, large_circles, small_circles): # 定义目标函数 R_sets = flex_pixel(image) results_big = find_big( - crop_blur, x_ratio, minR=R_sets[0], maxR=R_sets[1], width=width, p1=21, p2=32 + crop_blur, + x_ratio, + minR=R_sets[0], + maxR=R_sets[1], + width=width, + p1=21, + p2=32, ) results_small = find_small( - crop_small, x_ratio_small, x_ratio, minR=R_sets[2], maxR=R_sets[3], width=width + crop_small, + x_ratio_small, + x_ratio, + minR=R_sets[2], + maxR=R_sets[3], + width=width, ) circles = np.round(results_big).astype("int") diff --git a/recognize.py b/src/recognition/recognize.py similarity index 81% rename from recognize.py rename to src/recognition/recognize.py index 72ae417..d021439 100644 --- a/recognize.py +++ b/src/recognition/recognize.py @@ -5,15 +5,16 @@ from PIL import ImageGrab from rapidocr import RapidOCR, EngineType -from config import MONSTER_DATA, MONSTER_IMAGES, MONSTER_COUNT -import find_monster_zone -from winrt_capture import WinRTScreenCapture +from src.core.config import MONSTER_DATA, MONSTER_IMAGES, MONSTER_COUNT +from src.core.paths import ensure_tmp_images_dir, image_path +from . import find_monster_zone +from src.game.winrt_capture import WinRTScreenCapture logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) # 是否启用debug模式 -intelligent_workers_debug = True +INTELLIGENT_WORKERS_DEBUG = True # 数字区域相对坐标 relative_regions_nums = [ @@ -34,6 +35,7 @@ (0.8700, 0.05, 1.0000, 0.80), ] + def get_rapidocr_engine(prefer_gpu=False): """ prefer_gpu (bool): 是否优先尝试使用GPU @@ -41,6 +43,7 @@ def get_rapidocr_engine(prefer_gpu=False): try: if prefer_gpu: import torch + if torch.cuda.is_available(): return RapidOCR( params={ @@ -56,11 +59,21 @@ def get_rapidocr_engine(prefer_gpu=False): # 如果没有GPU可用,使用CPU onnxruntime return RapidOCR() + class RecognizeMonster: - ROI_RELATIVE = [(0.2464, 0.8410), (0.7542, 0.9510)] # 16:9下怪物区域相对坐标 - def __init__(self, method: str = "ADB", window_name: str | None = None, monitor_index: int | None = None): + ROI_RELATIVE = [ + (0.2464, 0.8410), + (0.7542, 0.9510), + ] # 16:9下怪物区域相对坐标 + + def __init__( + self, + method: str = "ADB", + window_name: str | None = None, + monitor_index: int | None = None, + ): self.method = method - self.main_roi = [(0, 0), (1919, 1079)] # 主区域坐标 + self.main_roi = [(0, 0), (1919, 1079)] # 主区域坐标 # 鼠标交互全局变量 self.roi_box = [] self.drawing = False @@ -87,12 +100,12 @@ def __init__(self, method: str = "ADB", window_name: str | None = None, monitor_ self.main_roi = [(0, 0), (w - 1, h - 1)] except Exception as e: logger.exception("WinRT capture init failed: %s", e) - self._winrt = None # 将 _winrt 设置为 None,表示初始化失败 - raise # 重新抛出异常,以便上层捕获 + self._winrt = None # 将 _winrt 设置为 None,表示初始化失败 + raise # 重新抛出异常,以便上层捕获 else: logger.info("WIN 模式未指定窗口或显示器,将使用 PIL 作为回退") - def mouse_callback(self, event, x:int, y:int, flags, param): + def mouse_callback(self, event, x: int, y: int, flags, param): if event == cv2.EVENT_LBUTTONDOWN: self.roi_box = [(x, y)] self.drawing = True @@ -122,8 +135,15 @@ def select_roi(self): return None # 添加操作提示 - cv2.putText(img, "Drag to select area | ENTER:confirm | ESC:retry", - (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) + cv2.putText( + img, + "Drag to select area | ENTER:confirm | ESC:retry", + (10, 30), + cv2.FONT_HERSHEY_SIMPLEX, + 0.7, + (0, 0, 255), + 2, + ) # 显示窗口 cv2.namedWindow("Select ROI", cv2.WINDOW_NORMAL) @@ -132,7 +152,7 @@ def select_roi(self): cv2.imshow("Select ROI", img) # 添加示例图片(要后弹出才看得见) - example_img = cv2.imread("images/eg.png") + example_img = cv2.imread(str(image_path("eg"))) # 显示示例图片在单独的窗口中 cv2.imshow("example", example_img) @@ -152,7 +172,9 @@ def select_roi(self): self.roi_box = [] continue - def find_best_match(target: cv2.typing.MatLike, ref_images: dict[int, cv2.typing.MatLike]): + def find_best_match( + target: cv2.typing.MatLike, ref_images: dict[int, cv2.typing.MatLike] + ): """ 模板匹配找到最佳匹配的参考图像 :param target: 目标图像 @@ -191,7 +213,7 @@ def get_manual_screenshot(self) -> cv2.typing.MatLike: screenshot = cv2.cvtColor(screenshot, cv2.COLOR_RGB2BGR) try: # 手动框选的截图需先识别目标区域 - cv2.imwrite(f"images/tmp/zone1.png", screenshot) + cv2.imwrite(str(ensure_tmp_images_dir() / "zone1.png"), screenshot) d_avatar, d_nums = find_monster_zone.cutFrame(screenshot) height, width, _ = screenshot.shape divisors = np.array([width, height, width, height]) @@ -206,7 +228,10 @@ def get_manual_screenshot(self) -> cv2.typing.MatLike: y_min = max(0, y_min) # 假如找到过能用main_roi的就存起来 logger.info(f"识别到目标区域:{[(x_min, y_min), (x_max, y_max)]}") - self.main_roi = [(x1 + x_min, y1 + y_min), (x1 + x_max, y1 + y_max)] + self.main_roi = [ + (x1 + x_min, y1 + y_min), + (x1 + x_max, y1 + y_max), + ] screenshot = screenshot[y_min:y_max, x_min:x_max] logger.info(f"区域更新为: {self.main_roi}") except Exception as e: @@ -257,9 +282,9 @@ def process_regions( main_height = screenshot.shape[0] main_width = screenshot.shape[1] - if intelligent_workers_debug: # 如果处于debug模式 + if INTELLIGENT_WORKERS_DEBUG: # 如果处于debug模式 # 存储模板图像用于debug - cv2.imwrite(f"images/tmp/zone.png", screenshot) + cv2.imwrite(str(ensure_tmp_images_dir() / "zone.png"), screenshot) # 遍历所有区域 for idx, rel in enumerate(relative_regions): @@ -274,14 +299,23 @@ def process_regions( sub_roi = screenshot[ry1:ry2, rx1:rx2] # 图像匹配 - matched_id, confidence = find_best_match(sub_roi, self.ref_images) - logger.info(f"target: {idx} matched_id: {matched_id}, confidence: {confidence:.4f}") + matched_id, confidence = find_best_match( + sub_roi, self.ref_images + ) + logger.info( + f"target: {idx} matched_id: {matched_id}, confidence: {confidence:.4f}" + ) if matched_id != 0 and confidence < matched_threshold: raise ValueError(f"模板匹配置信度过低: {confidence}") except Exception as e: logger.exception(f"区域 {idx} 匹配失败: {str(e)}") results.append( - {"region_id": idx, "matched_id": matched_id, "number": "N/A", "error": str(e)} + { + "region_id": idx, + "matched_id": matched_id, + "number": "N/A", + "error": str(e), + } ) continue try: @@ -295,20 +329,30 @@ def process_regions( # 提取OCR识别用的子区域 sub_roi_num = screenshot[ry1_num:ry2_num, rx1_num:rx2_num] processed = preprocess(sub_roi_num) # 二值化预处理 - processed = crop_to_min_bounding_rect(processed) # 去除多余黑框 - processed = add_black_border(processed, border_size=3) # 加上3像素黑框 + processed = crop_to_min_bounding_rect( + processed + ) # 去除多余黑框 + processed = add_black_border( + processed, border_size=3 + ) # 加上3像素黑框 # OCR识别(保留优化后的处理逻辑) number, ocr_confidence = self.do_num_ocr(processed) if number != "" and ocr_confidence < ocr_threshold: raise ValueError(f"OCR置信度过低: {ocr_confidence}") - if intelligent_workers_debug: # 如果处于debug模式 + if INTELLIGENT_WORKERS_DEBUG: # 如果处于debug模式 # 存储模板图像用于debug - cv2.imwrite(f"images/tmp/target_{idx}.png", sub_roi) + cv2.imwrite( + str(ensure_tmp_images_dir() / f"target_{idx}.png"), + sub_roi, + ) # 存储OCR图像用于debug - cv2.imwrite(f"images/tmp/number_{idx}.png", processed) + cv2.imwrite( + str(ensure_tmp_images_dir() / f"number_{idx}.png"), + processed, + ) if number == "" and matched_id != 0: raise ValueError("发现有怪物但无数量异常数据!") @@ -326,13 +370,22 @@ def process_regions( except Exception as e: logger.exception(f"区域 {idx} OCR识别失败: {str(e)}") results.append( - {"region_id": idx, "matched_id": matched_id, "number": "N/A", "error": str(e)} + { + "region_id": idx, + "matched_id": matched_id, + "number": "N/A", + "error": str(e), + } ) return results - + def do_num_ocr(self, img: cv2.typing.MatLike): - result = self.rapidocr_eng(img, use_det=False, use_cls=False, use_rec=True) - logger.info(f"OCR: text: '{result.txts[0]}', score: {result.scores[0]}") + result = self.rapidocr_eng( + img, use_det=False, use_cls=False, use_rec=True + ) + logger.info( + f"OCR: text: '{result.txts[0]}', score: {result.scores[0]}" + ) if result.txts[0] != "" and not result.txts[0].isdigit(): raise ValueError(f"OCR识别结果不是数字: '{result.txts[0]}'") return result.txts[0], result.scores[0] @@ -358,7 +411,9 @@ def crop_to_min_bounding_rect(image: cv2.typing.MatLike): else: gray = image # 寻找轮廓 - contours, _ = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + contours, _ = cv2.findContours( + gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE + ) # 如果没有找到轮廓就直接返回原图 if not contours: return image @@ -395,7 +450,9 @@ def preprocess(img: cv2.typing.MatLike): closed = bright_mask # 去除细小噪声:过滤不够大的连通区域 - contours, _ = cv2.findContours(closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + contours, _ = cv2.findContours( + closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE + ) for contour in contours: x, y, w, h = cv2.boundingRect(contour) if w <= 1: @@ -408,7 +465,9 @@ def preprocess(img: cv2.typing.MatLike): return closed -def find_best_match(target: cv2.typing.MatLike, ref_images: dict[int, cv2.typing.MatLike]): +def find_best_match( + target: cv2.typing.MatLike, ref_images: dict[int, cv2.typing.MatLike] +): """ 模板匹配找到最佳匹配的参考图像 :param target: 目标图像 @@ -444,30 +503,35 @@ def load_ref_images(ref_dir="images"): for i in range(MONSTER_COUNT + 1): # path = os.path.join(ref_dir, f"{i}.png") # if os.path.exists(path): - # img = cv2.imread(path, cv2.IMREAD_COLOR_BGR) + # img = cv2.imread(path, cv2.IMREAD_COLOR_BGR) if i == 0: img = MONSTER_IMAGES.get("empty") else: img = MONSTER_IMAGES.get(MONSTER_DATA["原始名称"][i]) if img is None: - logger.error(f"无法加载参考图片 i={i}, 名称={MONSTER_DATA['原始名称'][i] if i > 0 else 'empty'}") + logger.error( + f"无法加载参考图片 i={i}, 名称={MONSTER_DATA['原始名称'][i] if i > 0 else 'empty'}" + ) continue # 裁切模板匹配图像比例 img_crop = img[ - int(img.shape[0] * 0.16) : int(img.shape[0] * 0.80), # 高度取靠上部分 - int(img.shape[1] * 0.18) : int(img.shape[1] * 0.82), # 宽度与高度一致 + int(img.shape[0] * 0.16) : int( + img.shape[0] * 0.80 + ), # 高度取靠上部分 + int(img.shape[1] * 0.18) : int( + img.shape[1] * 0.82 + ), # 宽度与高度一致 ] # 调整参考图像大小以匹配目标图像 ref_resized = cv2.resize(img_crop, (74, 74)) ref_resized = ref_resized[0:70, :] - if intelligent_workers_debug: # 如果处于debug模式 + if INTELLIGENT_WORKERS_DEBUG: # 如果处于debug模式 # 存储模板图像用于debug - if not os.path.exists("images/tmp"): - os.makedirs("images/tmp") - cv2.imwrite(f"images/tmp/xref_{i}.png", ref_resized) + tmp_dir = ensure_tmp_images_dir() + cv2.imwrite(str(tmp_dir / f"xref_{i}.png"), ref_resized) ref_images[i] = ref_resized return ref_images diff --git a/src/recognition/specialmonster.py b/src/recognition/specialmonster.py new file mode 100644 index 0000000..31b9eb5 --- /dev/null +++ b/src/recognition/specialmonster.py @@ -0,0 +1,44 @@ +class SpecialMonsterHandler: + """ + 可按照格式自行添加特殊怪物的留言信息: + 格式: + 怪物ID: { + 'name': '怪物名称', # 可留空 + 'win_message': '胜利时显示的消息', # 可留空 + 'lose_message': '失败时显示的消息', # 可留空 + } + """ + + def __init__(self): + self.special_monsters = { + 1: { + "name": "狗神", + "win_message": "全军出击,我咬死你!", + "lose_message": "牙崩了牙崩了", + }, + } + + def check_special_monsters(self, left_monsters, right_monsters, winner): + messages = [] + + for monster_id, config in self.special_monsters.items(): + left_has = ( + left_monsters[str(monster_id)].text().isdigit() + and int(left_monsters[str(monster_id)].text()) > 0 + ) + right_has = ( + right_monsters[str(monster_id)].text().isdigit() + and int(right_monsters[str(monster_id)].text()) > 0 + ) + + if left_has or right_has: + if winner == "左方" and left_has and config["win_message"]: + messages.append(config["win_message"]) + elif winner == "右方" and right_has and config["win_message"]: + messages.append(config["win_message"]) + elif winner == "右方" and left_has and config["lose_message"]: + messages.append(config["lose_message"]) + elif winner == "左方" and right_has and config["lose_message"]: + messages.append(config["lose_message"]) + + return "\n".join(messages) diff --git a/ico/background.png b/src/resources/assets/icons/background.png similarity index 100% rename from ico/background.png rename to src/resources/assets/icons/background.png diff --git a/ico/icon.ico b/src/resources/assets/icons/icon.ico similarity index 100% rename from ico/icon.ico rename to src/resources/assets/icons/icon.ico diff --git a/ico/icon_64x64.ico b/src/resources/assets/icons/icon_64x64.ico similarity index 100% rename from ico/icon_64x64.ico rename to src/resources/assets/icons/icon_64x64.ico diff --git "a/images/R-11\347\252\201\345\207\273\345\212\250\345\212\233\350\243\205\347\224\262.png" "b/src/resources/assets/images/R-11\347\252\201\345\207\273\345\212\250\345\212\233\350\243\205\347\224\262.png" similarity index 100% rename from "images/R-11\347\252\201\345\207\273\345\212\250\345\212\233\350\243\205\347\224\262.png" rename to "src/resources/assets/images/R-11\347\252\201\345\207\273\345\212\250\345\212\233\350\243\205\347\224\262.png" diff --git "a/images/R-31\351\207\215\345\236\213\345\212\250\345\212\233\350\243\205\347\224\262.png" "b/src/resources/assets/images/R-31\351\207\215\345\236\213\345\212\250\345\212\233\350\243\205\347\224\262.png" similarity index 100% rename from "images/R-31\351\207\215\345\236\213\345\212\250\345\212\233\350\243\205\347\224\262.png" rename to "src/resources/assets/images/R-31\351\207\215\345\236\213\345\212\250\345\212\233\350\243\205\347\224\262.png" diff --git a/images/eg.png b/src/resources/assets/images/eg.png similarity index 100% rename from images/eg.png rename to src/resources/assets/images/eg.png diff --git a/images/empty.png b/src/resources/assets/images/empty.png similarity index 100% rename from images/empty.png rename to src/resources/assets/images/empty.png diff --git a/images/login/announcement_close.png b/src/resources/assets/images/login/announcement_close.png similarity index 100% rename from images/login/announcement_close.png rename to src/resources/assets/images/login/announcement_close.png diff --git a/images/login/competition_page.png b/src/resources/assets/images/login/competition_page.png similarity index 100% rename from images/login/competition_page.png rename to src/resources/assets/images/login/competition_page.png diff --git a/images/login/event_claim_close.png b/src/resources/assets/images/login/event_claim_close.png similarity index 100% rename from images/login/event_claim_close.png rename to src/resources/assets/images/login/event_claim_close.png diff --git a/images/login/login_button.png b/src/resources/assets/images/login/login_button.png similarity index 100% rename from images/login/login_button.png rename to src/resources/assets/images/login/login_button.png diff --git a/images/login/login_off.png b/src/resources/assets/images/login/login_off.png similarity index 100% rename from images/login/login_off.png rename to src/resources/assets/images/login/login_off.png diff --git a/images/process/0.png b/src/resources/assets/images/process/0.png similarity index 100% rename from images/process/0.png rename to src/resources/assets/images/process/0.png diff --git a/images/process/1.png b/src/resources/assets/images/process/1.png similarity index 100% rename from images/process/1.png rename to src/resources/assets/images/process/1.png diff --git a/images/process/10.png b/src/resources/assets/images/process/10.png similarity index 100% rename from images/process/10.png rename to src/resources/assets/images/process/10.png diff --git a/images/process/11.png b/src/resources/assets/images/process/11.png similarity index 100% rename from images/process/11.png rename to src/resources/assets/images/process/11.png diff --git a/images/process/12.png b/src/resources/assets/images/process/12.png similarity index 100% rename from images/process/12.png rename to src/resources/assets/images/process/12.png diff --git a/images/process/13.png b/src/resources/assets/images/process/13.png similarity index 100% rename from images/process/13.png rename to src/resources/assets/images/process/13.png diff --git a/images/process/14.png b/src/resources/assets/images/process/14.png similarity index 100% rename from images/process/14.png rename to src/resources/assets/images/process/14.png diff --git a/images/process/15.png b/src/resources/assets/images/process/15.png similarity index 100% rename from images/process/15.png rename to src/resources/assets/images/process/15.png diff --git a/images/process/1746774840_28_5.png1s.png b/src/resources/assets/images/process/1746774840_28_5.png1s.png similarity index 100% rename from images/process/1746774840_28_5.png1s.png rename to src/resources/assets/images/process/1746774840_28_5.png1s.png diff --git a/images/process/2.png b/src/resources/assets/images/process/2.png similarity index 100% rename from images/process/2.png rename to src/resources/assets/images/process/2.png diff --git a/images/process/3.png b/src/resources/assets/images/process/3.png similarity index 100% rename from images/process/3.png rename to src/resources/assets/images/process/3.png diff --git a/images/process/4.png b/src/resources/assets/images/process/4.png similarity index 100% rename from images/process/4.png rename to src/resources/assets/images/process/4.png diff --git a/images/process/5.png b/src/resources/assets/images/process/5.png similarity index 100% rename from images/process/5.png rename to src/resources/assets/images/process/5.png diff --git a/images/process/6.png b/src/resources/assets/images/process/6.png similarity index 100% rename from images/process/6.png rename to src/resources/assets/images/process/6.png diff --git a/images/process/7.png b/src/resources/assets/images/process/7.png similarity index 100% rename from images/process/7.png rename to src/resources/assets/images/process/7.png diff --git a/images/process/8.png b/src/resources/assets/images/process/8.png similarity index 100% rename from images/process/8.png rename to src/resources/assets/images/process/8.png diff --git a/images/process/9.png b/src/resources/assets/images/process/9.png similarity index 100% rename from images/process/9.png rename to src/resources/assets/images/process/9.png diff --git "a/images/\342\200\234\345\244\215\344\273\207\350\200\205\342\200\235.png" "b/src/resources/assets/images/\342\200\234\345\244\215\344\273\207\350\200\205\342\200\235.png" similarity index 100% rename from "images/\342\200\234\345\244\215\344\273\207\350\200\205\342\200\235.png" rename to "src/resources/assets/images/\342\200\234\345\244\215\344\273\207\350\200\205\342\200\235.png" diff --git "a/images/\342\200\234\345\246\222\342\200\235.png" "b/src/resources/assets/images/\342\200\234\345\246\222\342\200\235.png" similarity index 100% rename from "images/\342\200\234\345\246\222\342\200\235.png" rename to "src/resources/assets/images/\342\200\234\345\246\222\342\200\235.png" diff --git "a/images/\342\200\234\345\267\242\347\251\264\342\200\235.png" "b/src/resources/assets/images/\342\200\234\345\267\242\347\251\264\342\200\235.png" similarity index 100% rename from "images/\342\200\234\345\267\242\347\251\264\342\200\235.png" rename to "src/resources/assets/images/\342\200\234\345\267\242\347\251\264\342\200\235.png" diff --git "a/images/\342\200\234\345\272\236\350\264\235\342\200\235.png" "b/src/resources/assets/images/\342\200\234\345\272\236\350\264\235\342\200\235.png" similarity index 100% rename from "images/\342\200\234\345\272\236\350\264\235\342\200\235.png" rename to "src/resources/assets/images/\342\200\234\345\272\236\350\264\235\342\200\235.png" diff --git "a/images/\342\200\234\346\212\225\347\237\263\346\234\272\342\200\235.png" "b/src/resources/assets/images/\342\200\234\346\212\225\347\237\263\346\234\272\342\200\235.png" similarity index 100% rename from "images/\342\200\234\346\212\225\347\237\263\346\234\272\342\200\235.png" rename to "src/resources/assets/images/\342\200\234\346\212\225\347\237\263\346\234\272\342\200\235.png" diff --git "a/images/\342\200\234\347\233\233\346\200\222\342\200\235.png" "b/src/resources/assets/images/\342\200\234\347\233\233\346\200\222\342\200\235.png" similarity index 100% rename from "images/\342\200\234\347\233\233\346\200\222\342\200\235.png" rename to "src/resources/assets/images/\342\200\234\347\233\233\346\200\222\342\200\235.png" diff --git "a/images/\342\200\234\350\207\252\345\234\250\342\200\235.png" "b/src/resources/assets/images/\342\200\234\350\207\252\345\234\250\342\200\235.png" similarity index 100% rename from "images/\342\200\234\350\207\252\345\234\250\342\200\235.png" rename to "src/resources/assets/images/\342\200\234\350\207\252\345\234\250\342\200\235.png" diff --git "a/images/\342\200\234\350\220\250\347\261\263\347\232\204\346\204\217\345\277\227\342\200\235.png" "b/src/resources/assets/images/\342\200\234\350\220\250\347\261\263\347\232\204\346\204\217\345\277\227\342\200\235.png" similarity index 100% rename from "images/\342\200\234\350\220\250\347\261\263\347\232\204\346\204\217\345\277\227\342\200\235.png" rename to "src/resources/assets/images/\342\200\234\350\220\250\347\261\263\347\232\204\346\204\217\345\277\227\342\200\235.png" diff --git "a/images/\342\200\234\351\222\263\351\222\263\347\224\237\351\243\216\342\200\235.png" "b/src/resources/assets/images/\342\200\234\351\222\263\351\222\263\347\224\237\351\243\216\342\200\235.png" similarity index 100% rename from "images/\342\200\234\351\222\263\351\222\263\347\224\237\351\243\216\342\200\235.png" rename to "src/resources/assets/images/\342\200\234\351\222\263\351\222\263\347\224\237\351\243\216\342\200\235.png" diff --git "a/images/\342\200\234\351\227\250\342\200\235.png" "b/src/resources/assets/images/\342\200\234\351\227\250\342\200\235.png" similarity index 100% rename from "images/\342\200\234\351\227\250\342\200\235.png" rename to "src/resources/assets/images/\342\200\234\351\227\250\342\200\235.png" diff --git "a/images/\342\200\234\351\230\277\345\222\254\342\200\235.png" "b/src/resources/assets/images/\342\200\234\351\230\277\345\222\254\342\200\235.png" similarity index 100% rename from "images/\342\200\234\351\230\277\345\222\254\342\200\235.png" rename to "src/resources/assets/images/\342\200\234\351\230\277\345\222\254\342\200\235.png" diff --git "a/images/\344\271\214\350\220\250\346\226\257\347\252\201\350\242\255\345\274\251\346\211\213.png" "b/src/resources/assets/images/\344\271\214\350\220\250\346\226\257\347\252\201\350\242\255\345\274\251\346\211\213.png" similarity index 100% rename from "images/\344\271\214\350\220\250\346\226\257\347\252\201\350\242\255\345\274\251\346\211\213.png" rename to "src/resources/assets/images/\344\271\214\350\220\250\346\226\257\347\252\201\350\242\255\345\274\251\346\211\213.png" diff --git "a/images/\345\205\250\345\260\201\351\227\255\346\262\231\346\273\251\350\275\246.png" "b/src/resources/assets/images/\345\205\250\345\260\201\351\227\255\346\262\231\346\273\251\350\275\246.png" similarity index 100% rename from "images/\345\205\250\345\260\201\351\227\255\346\262\231\346\273\251\350\275\246.png" rename to "src/resources/assets/images/\345\205\250\345\260\201\351\227\255\346\262\231\346\273\251\350\275\246.png" diff --git "a/images/\345\206\260\345\216\237\345\215\253\346\263\225\350\200\205.png" "b/src/resources/assets/images/\345\206\260\345\216\237\345\215\253\346\263\225\350\200\205.png" similarity index 100% rename from "images/\345\206\260\345\216\237\345\215\253\346\263\225\350\200\205.png" rename to "src/resources/assets/images/\345\206\260\345\216\237\345\215\253\346\263\225\350\200\205.png" diff --git "a/images/\345\206\260\345\216\237\346\234\257\345\270\210.png" "b/src/resources/assets/images/\345\206\260\345\216\237\346\234\257\345\270\210.png" similarity index 100% rename from "images/\345\206\260\345\216\237\346\234\257\345\270\210.png" rename to "src/resources/assets/images/\345\206\260\345\216\237\346\234\257\345\270\210.png" diff --git "a/images/\345\206\260\347\210\206\346\272\220\347\237\263\350\231\253.png" "b/src/resources/assets/images/\345\206\260\347\210\206\346\272\220\347\237\263\350\231\253.png" similarity index 100% rename from "images/\345\206\260\347\210\206\346\272\220\347\237\263\350\231\253.png" rename to "src/resources/assets/images/\345\206\260\347\210\206\346\272\220\347\237\263\350\231\253.png" diff --git "a/images/\345\207\213\351\233\266\351\252\221\345\243\253.png" "b/src/resources/assets/images/\345\207\213\351\233\266\351\252\221\345\243\253.png" similarity index 100% rename from "images/\345\207\213\351\233\266\351\252\221\345\243\253.png" rename to "src/resources/assets/images/\345\207\213\351\233\266\351\252\221\345\243\253.png" diff --git "a/images/\345\207\266\350\261\225\345\205\275.png" "b/src/resources/assets/images/\345\207\266\350\261\225\345\205\275.png" similarity index 100% rename from "images/\345\207\266\350\261\225\345\205\275.png" rename to "src/resources/assets/images/\345\207\266\350\261\225\345\205\275.png" diff --git "a/images/\345\217\215\350\243\205\347\224\262\346\255\245\345\205\265.png" "b/src/resources/assets/images/\345\217\215\350\243\205\347\224\262\346\255\245\345\205\265.png" similarity index 100% rename from "images/\345\217\215\350\243\205\347\224\262\346\255\245\345\205\265.png" rename to "src/resources/assets/images/\345\217\215\350\243\205\347\224\262\346\255\245\345\205\265.png" diff --git "a/images/\345\217\230\345\274\202\345\267\250\345\262\251\350\233\233.png" "b/src/resources/assets/images/\345\217\230\345\274\202\345\267\250\345\262\251\350\233\233.png" similarity index 100% rename from "images/\345\217\230\345\274\202\345\267\250\345\262\251\350\233\233.png" rename to "src/resources/assets/images/\345\217\230\345\274\202\345\267\250\345\262\251\350\233\233.png" diff --git "a/images/\345\221\274\345\225\270\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222.png" "b/src/resources/assets/images/\345\221\274\345\225\270\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222.png" similarity index 100% rename from "images/\345\221\274\345\225\270\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222.png" rename to "src/resources/assets/images/\345\221\274\345\225\270\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222.png" diff --git "a/images/\345\233\272\346\265\267\345\207\277\347\237\263\350\200\205.png" "b/src/resources/assets/images/\345\233\272\346\265\267\345\207\277\347\237\263\350\200\205.png" similarity index 100% rename from "images/\345\233\272\346\265\267\345\207\277\347\237\263\350\200\205.png" rename to "src/resources/assets/images/\345\233\272\346\265\267\345\207\277\347\237\263\350\200\205.png" diff --git "a/images/\345\244\215\344\273\207\350\200\205.png" "b/src/resources/assets/images/\345\244\215\344\273\207\350\200\205.png" similarity index 100% rename from "images/\345\244\215\344\273\207\350\200\205.png" rename to "src/resources/assets/images/\345\244\215\344\273\207\350\200\205.png" diff --git "a/images/\345\245\216\351\232\206\357\274\214\346\221\251\350\257\203\350\220\250\345\237\265\346\235\203\345\214\226.png" "b/src/resources/assets/images/\345\245\216\351\232\206\357\274\214\346\221\251\350\257\203\350\220\250\345\237\265\346\235\203\345\214\226.png" similarity index 100% rename from "images/\345\245\216\351\232\206\357\274\214\346\221\251\350\257\203\350\220\250\345\237\265\346\235\203\345\214\226.png" rename to "src/resources/assets/images/\345\245\216\351\232\206\357\274\214\346\221\251\350\257\203\350\220\250\345\237\265\346\235\203\345\214\226.png" diff --git "a/images/\345\256\277\344\270\273\346\265\201\346\265\252\350\200\205.png" "b/src/resources/assets/images/\345\256\277\344\270\273\346\265\201\346\265\252\350\200\205.png" similarity index 100% rename from "images/\345\256\277\344\270\273\346\265\201\346\265\252\350\200\205.png" rename to "src/resources/assets/images/\345\256\277\344\270\273\346\265\201\346\265\252\350\200\205.png" diff --git "a/images/\345\257\273\350\267\257\350\200\205\344\277\241\344\275\277.png" "b/src/resources/assets/images/\345\257\273\350\267\257\350\200\205\344\277\241\344\275\277.png" similarity index 100% rename from "images/\345\257\273\350\267\257\350\200\205\344\277\241\344\275\277.png" rename to "src/resources/assets/images/\345\257\273\350\267\257\350\200\205\344\277\241\344\275\277.png" diff --git "a/images/\345\261\261\346\265\267\344\274\227\347\252\245\351\255\205\344\272\272.png" "b/src/resources/assets/images/\345\261\261\346\265\267\344\274\227\347\252\245\351\255\205\344\272\272.png" similarity index 100% rename from "images/\345\261\261\346\265\267\344\274\227\347\252\245\351\255\205\344\272\272.png" rename to "src/resources/assets/images/\345\261\261\346\265\267\344\274\227\347\252\245\351\255\205\344\272\272.png" diff --git "a/images/\345\262\201\347\233\270.png" "b/src/resources/assets/images/\345\262\201\347\233\270.png" similarity index 100% rename from "images/\345\262\201\347\233\270.png" rename to "src/resources/assets/images/\345\262\201\347\233\270.png" diff --git "a/images/\345\262\251\345\206\240\345\205\275.png" "b/src/resources/assets/images/\345\262\251\345\206\240\345\205\275.png" similarity index 100% rename from "images/\345\262\251\345\206\240\345\205\275.png" rename to "src/resources/assets/images/\345\262\251\345\206\240\345\205\275.png" diff --git "a/images/\345\274\247\345\205\211\351\224\213\345\215\253\351\225\277.png" "b/src/resources/assets/images/\345\274\247\345\205\211\351\224\213\345\215\253\351\225\277.png" similarity index 100% rename from "images/\345\274\247\345\205\211\351\224\213\345\215\253\351\225\277.png" rename to "src/resources/assets/images/\345\274\247\345\205\211\351\224\213\345\215\253\351\225\277.png" diff --git "a/images/\346\213\263\346\211\213\345\233\232\347\212\257.png" "b/src/resources/assets/images/\346\213\263\346\211\213\345\233\232\347\212\257.png" similarity index 100% rename from "images/\346\213\263\346\211\213\345\233\232\347\212\257.png" rename to "src/resources/assets/images/\346\213\263\346\211\213\345\233\232\347\212\257.png" diff --git "a/images/\346\217\220\344\272\232\345\215\241\344\271\214\345\245\275\346\210\230\350\200\205.png" "b/src/resources/assets/images/\346\217\220\344\272\232\345\215\241\344\271\214\345\245\275\346\210\230\350\200\205.png" similarity index 100% rename from "images/\346\217\220\344\272\232\345\215\241\344\271\214\345\245\275\346\210\230\350\200\205.png" rename to "src/resources/assets/images/\346\217\220\344\272\232\345\215\241\344\271\214\345\245\275\346\210\230\350\200\205.png" diff --git "a/images/\346\217\220\344\272\232\345\215\241\344\271\214\347\240\264\345\235\217\347\216\213.png" "b/src/resources/assets/images/\346\217\220\344\272\232\345\215\241\344\271\214\347\240\264\345\235\217\347\216\213.png" similarity index 100% rename from "images/\346\217\220\344\272\232\345\215\241\344\271\214\347\240\264\345\235\217\347\216\213.png" rename to "src/resources/assets/images/\346\217\220\344\272\232\345\215\241\344\271\214\347\240\264\345\235\217\347\216\213.png" diff --git "a/images/\346\225\243\345\215\216\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222.png" "b/src/resources/assets/images/\346\225\243\345\215\216\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222.png" similarity index 100% rename from "images/\346\225\243\345\215\216\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222.png" rename to "src/resources/assets/images/\346\225\243\345\215\216\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222.png" diff --git "a/images/\346\227\272\350\264\242.png" "b/src/resources/assets/images/\346\227\272\350\264\242.png" similarity index 100% rename from "images/\346\227\272\350\264\242.png" rename to "src/resources/assets/images/\346\227\272\350\264\242.png" diff --git "a/images/\346\232\264\350\265\260\351\243\237\344\272\272\350\212\261.png" "b/src/resources/assets/images/\346\232\264\350\265\260\351\243\237\344\272\272\350\212\261.png" similarity index 100% rename from "images/\346\232\264\350\265\260\351\243\237\344\272\272\350\212\261.png" rename to "src/resources/assets/images/\346\232\264\350\265\260\351\243\237\344\272\272\350\212\261.png" diff --git "a/images/\346\232\264\350\265\260\351\243\237\350\231\253\350\212\261.png" "b/src/resources/assets/images/\346\232\264\350\265\260\351\243\237\350\231\253\350\212\261.png" similarity index 100% rename from "images/\346\232\264\350\265\260\351\243\237\350\231\253\350\212\261.png" rename to "src/resources/assets/images/\346\232\264\350\265\260\351\243\237\350\231\253\350\212\261.png" diff --git "a/images/\346\234\227\345\247\206\351\205\222\346\216\250\350\215\220\350\200\205.png" "b/src/resources/assets/images/\346\234\227\345\247\206\351\205\222\346\216\250\350\215\220\350\200\205.png" similarity index 100% rename from "images/\346\234\227\345\247\206\351\205\222\346\216\250\350\215\220\350\200\205.png" rename to "src/resources/assets/images/\346\234\227\345\247\206\351\205\222\346\216\250\350\215\220\350\200\205.png" diff --git "a/images/\346\235\260\346\226\257\351\241\277\302\267\345\250\201\345\273\211\345\247\206\346\226\257.png" "b/src/resources/assets/images/\346\235\260\346\226\257\351\241\277\302\267\345\250\201\345\273\211\345\247\206\346\226\257.png" similarity index 100% rename from "images/\346\235\260\346\226\257\351\241\277\302\267\345\250\201\345\273\211\345\247\206\346\226\257.png" rename to "src/resources/assets/images/\346\235\260\346\226\257\351\241\277\302\267\345\250\201\345\273\211\345\247\206\346\226\257.png" diff --git "a/images/\346\236\201\346\270\251\350\222\270\346\261\275\346\210\230\350\275\246.png" "b/src/resources/assets/images/\346\236\201\346\270\251\350\222\270\346\261\275\346\210\230\350\275\246.png" similarity index 100% rename from "images/\346\236\201\346\270\251\350\222\270\346\261\275\346\210\230\350\275\246.png" rename to "src/resources/assets/images/\346\236\201\346\270\251\350\222\270\346\261\275\346\210\230\350\275\246.png" diff --git "a/images/\346\236\243\345\244\247\345\210\200.png" "b/src/resources/assets/images/\346\236\243\345\244\247\345\210\200.png" similarity index 100% rename from "images/\346\236\243\345\244\247\345\210\200.png" rename to "src/resources/assets/images/\346\236\243\345\244\247\345\210\200.png" diff --git "a/images/\346\236\257\346\234\275\344\271\213\347\247\215.png" "b/src/resources/assets/images/\346\236\257\346\234\275\344\271\213\347\247\215.png" similarity index 100% rename from "images/\346\236\257\346\234\275\344\271\213\347\247\215.png" rename to "src/resources/assets/images/\346\236\257\346\234\275\344\271\213\347\247\215.png" diff --git "a/images/\346\243\230\345\205\275.png" "b/src/resources/assets/images/\346\243\230\345\205\275.png" similarity index 100% rename from "images/\346\243\230\345\205\275.png" rename to "src/resources/assets/images/\346\243\230\345\205\275.png" diff --git "a/images/\346\256\213\345\205\232\344\271\220\345\233\242\351\274\223\346\211\213.png" "b/src/resources/assets/images/\346\256\213\345\205\232\344\271\220\345\233\242\351\274\223\346\211\213.png" similarity index 100% rename from "images/\346\256\213\345\205\232\344\271\220\345\233\242\351\274\223\346\211\213.png" rename to "src/resources/assets/images/\346\256\213\345\205\232\344\271\220\345\233\242\351\274\223\346\211\213.png" diff --git "a/images/\346\256\213\345\205\232\350\220\250\345\205\213\346\226\257\346\211\213.png" "b/src/resources/assets/images/\346\256\213\345\205\232\350\220\250\345\205\213\346\226\257\346\211\213.png" similarity index 100% rename from "images/\346\256\213\345\205\232\350\220\250\345\205\213\346\226\257\346\211\213.png" rename to "src/resources/assets/images/\346\256\213\345\205\232\350\220\250\345\205\213\346\226\257\346\211\213.png" diff --git "a/images/\346\260\264\346\211\213\351\207\215\350\211\207.png" "b/src/resources/assets/images/\346\260\264\346\211\213\351\207\215\350\211\207.png" similarity index 100% rename from "images/\346\260\264\346\211\213\351\207\215\350\211\207.png" rename to "src/resources/assets/images/\346\260\264\346\211\213\351\207\215\350\211\207.png" diff --git "a/images/\346\260\264\351\201\201\345\277\215\350\200\205.png" "b/src/resources/assets/images/\346\260\264\351\201\201\345\277\215\350\200\205.png" similarity index 100% rename from "images/\346\260\264\351\201\201\345\277\215\350\200\205.png" rename to "src/resources/assets/images/\346\260\264\351\201\201\345\277\215\350\200\205.png" diff --git "a/images/\346\262\211\346\262\231.png" "b/src/resources/assets/images/\346\262\211\346\262\231.png" similarity index 100% rename from "images/\346\262\211\346\262\231.png" rename to "src/resources/assets/images/\346\262\211\346\262\231.png" diff --git "a/images/\346\262\270\350\241\200\351\252\221\345\243\253\345\233\242\347\262\276\351\224\220.png" "b/src/resources/assets/images/\346\262\270\350\241\200\351\252\221\345\243\253\345\233\242\347\262\276\351\224\220.png" similarity index 100% rename from "images/\346\262\270\350\241\200\351\252\221\345\243\253\345\233\242\347\262\276\351\224\220.png" rename to "src/resources/assets/images/\346\262\270\350\241\200\351\252\221\345\243\253\345\233\242\347\262\276\351\224\220.png" diff --git "a/images/\346\263\245\345\262\251\345\260\217\351\230\237\350\267\265\350\241\214\350\200\205.png" "b/src/resources/assets/images/\346\263\245\345\262\251\345\260\217\351\230\237\350\267\265\350\241\214\350\200\205.png" similarity index 100% rename from "images/\346\263\245\345\262\251\345\260\217\351\230\237\350\267\265\350\241\214\350\200\205.png" rename to "src/resources/assets/images/\346\263\245\345\262\251\345\260\217\351\230\237\350\267\265\350\241\214\350\200\205.png" diff --git "a/images/\346\263\245\345\262\251\345\267\250\345\203\217.png" "b/src/resources/assets/images/\346\263\245\345\262\251\345\267\250\345\203\217.png" similarity index 100% rename from "images/\346\263\245\345\262\251\345\267\250\345\203\217.png" rename to "src/resources/assets/images/\346\263\245\345\262\251\345\267\250\345\203\217.png" diff --git "a/images/\346\267\261\346\272\237\350\243\202\347\244\201\350\200\205.png" "b/src/resources/assets/images/\346\267\261\346\272\237\350\243\202\347\244\201\350\200\205.png" similarity index 100% rename from "images/\346\267\261\346\272\237\350\243\202\347\244\201\350\200\205.png" rename to "src/resources/assets/images/\346\267\261\346\272\237\350\243\202\347\244\201\350\200\205.png" diff --git "a/images/\346\270\251\351\241\272\347\232\204\346\255\246\350\243\205\351\251\256\345\205\275.png" "b/src/resources/assets/images/\346\270\251\351\241\272\347\232\204\346\255\246\350\243\205\351\251\256\345\205\275.png" similarity index 100% rename from "images/\346\270\251\351\241\272\347\232\204\346\255\246\350\243\205\351\251\256\345\205\275.png" rename to "src/resources/assets/images/\346\270\251\351\241\272\347\232\204\346\255\246\350\243\205\351\251\256\345\205\275.png" diff --git "a/images/\346\270\270\345\207\273\351\230\237\347\233\276\345\215\253.png" "b/src/resources/assets/images/\346\270\270\345\207\273\351\230\237\347\233\276\345\215\253.png" similarity index 100% rename from "images/\346\270\270\345\207\273\351\230\237\347\233\276\345\215\253.png" rename to "src/resources/assets/images/\346\270\270\345\207\273\351\230\237\347\233\276\345\215\253.png" diff --git "a/images/\346\271\226\347\225\224\345\277\227\346\204\277\350\200\205.png" "b/src/resources/assets/images/\346\271\226\347\225\224\345\277\227\346\204\277\350\200\205.png" similarity index 100% rename from "images/\346\271\226\347\225\224\345\277\227\346\204\277\350\200\205.png" rename to "src/resources/assets/images/\346\271\226\347\225\224\345\277\227\346\204\277\350\200\205.png" diff --git "a/images/\346\272\220\347\237\263\347\225\270\345\217\230\344\275\223.png" "b/src/resources/assets/images/\346\272\220\347\237\263\347\225\270\345\217\230\344\275\223.png" similarity index 100% rename from "images/\346\272\220\347\237\263\347\225\270\345\217\230\344\275\223.png" rename to "src/resources/assets/images/\346\272\220\347\237\263\347\225\270\345\217\230\344\275\223.png" diff --git "a/images/\347\201\274\347\203\255\346\272\220\347\237\263\350\231\253.png" "b/src/resources/assets/images/\347\201\274\347\203\255\346\272\220\347\237\263\350\231\253.png" similarity index 100% rename from "images/\347\201\274\347\203\255\346\272\220\347\237\263\350\231\253.png" rename to "src/resources/assets/images/\347\201\274\347\203\255\346\272\220\347\237\263\350\231\253.png" diff --git "a/images/\347\202\255\351\225\277\347\237\233.png" "b/src/resources/assets/images/\347\202\255\351\225\277\347\237\233.png" similarity index 100% rename from "images/\347\202\255\351\225\277\347\237\233.png" rename to "src/resources/assets/images/\347\202\255\351\225\277\347\237\233.png" diff --git "a/images/\347\202\256\345\207\273\347\273\204\351\225\277.png" "b/src/resources/assets/images/\347\202\256\345\207\273\347\273\204\351\225\277.png" similarity index 100% rename from "images/\347\202\256\345\207\273\347\273\204\351\225\277.png" rename to "src/resources/assets/images/\347\202\256\345\207\273\347\273\204\351\225\277.png" diff --git "a/images/\347\202\275\347\204\260\346\272\220\347\237\263\350\231\253.png" "b/src/resources/assets/images/\347\202\275\347\204\260\346\272\220\347\237\263\350\231\253.png" similarity index 100% rename from "images/\347\202\275\347\204\260\346\272\220\347\237\263\350\231\253.png" rename to "src/resources/assets/images/\347\202\275\347\204\260\346\272\220\347\237\263\350\231\253.png" diff --git "a/images/\347\213\202\346\232\264\345\256\277\344\270\273\347\273\204\351\225\277.png" "b/src/resources/assets/images/\347\213\202\346\232\264\345\256\277\344\270\273\347\273\204\351\225\277.png" similarity index 100% rename from "images/\347\213\202\346\232\264\345\256\277\344\270\273\347\273\204\351\225\277.png" rename to "src/resources/assets/images/\347\213\202\346\232\264\345\256\277\344\270\273\347\273\204\351\225\277.png" diff --git "a/images/\347\213\202\346\232\264\347\232\204\347\214\216\347\213\227pro.png" "b/src/resources/assets/images/\347\213\202\346\232\264\347\232\204\347\214\216\347\213\227pro.png" similarity index 100% rename from "images/\347\213\202\346\232\264\347\232\204\347\214\216\347\213\227pro.png" rename to "src/resources/assets/images/\347\213\202\346\232\264\347\232\204\347\214\216\347\213\227pro.png" diff --git "a/images/\347\213\231\345\207\273\346\255\245\345\205\265.png" "b/src/resources/assets/images/\347\213\231\345\207\273\346\255\245\345\205\265.png" similarity index 100% rename from "images/\347\213\231\345\207\273\346\255\245\345\205\265.png" rename to "src/resources/assets/images/\347\213\231\345\207\273\346\255\245\345\205\265.png" diff --git "a/images/\347\216\211\345\217\214\345\211\221.png" "b/src/resources/assets/images/\347\216\211\345\217\214\345\211\221.png" similarity index 100% rename from "images/\347\216\211\345\217\214\345\211\221.png" rename to "src/resources/assets/images/\347\216\211\345\217\214\345\211\221.png" diff --git "a/images/\347\224\260\351\274\267.png" "b/src/resources/assets/images/\347\224\260\351\274\267.png" similarity index 100% rename from "images/\347\224\260\351\274\267.png" rename to "src/resources/assets/images/\347\224\260\351\274\267.png" diff --git "a/images/\347\225\270\345\217\230\350\265\230\347\224\237\347\211\251.png" "b/src/resources/assets/images/\347\225\270\345\217\230\350\265\230\347\224\237\347\211\251.png" similarity index 100% rename from "images/\347\225\270\345\217\230\350\265\230\347\224\237\347\211\251.png" rename to "src/resources/assets/images/\347\225\270\345\217\230\350\265\230\347\224\237\347\211\251.png" diff --git "a/images/\347\230\264.png" "b/src/resources/assets/images/\347\230\264.png" similarity index 100% rename from "images/\347\230\264.png" rename to "src/resources/assets/images/\347\230\264.png" diff --git "a/images/\347\237\277\350\204\211\345\256\210\345\215\253.png" "b/src/resources/assets/images/\347\237\277\350\204\211\345\256\210\345\215\253.png" similarity index 100% rename from "images/\347\237\277\350\204\211\345\256\210\345\215\253.png" rename to "src/resources/assets/images/\347\237\277\350\204\211\345\256\210\345\215\253.png" diff --git "a/images/\347\240\201\345\244\264\346\260\264\346\211\213.png" "b/src/resources/assets/images/\347\240\201\345\244\264\346\260\264\346\211\213.png" similarity index 100% rename from "images/\347\240\201\345\244\264\346\260\264\346\211\213.png" rename to "src/resources/assets/images/\347\240\201\345\244\264\346\260\264\346\211\213.png" diff --git "a/images/\347\241\254\347\224\262\347\210\252\345\205\275.png" "b/src/resources/assets/images/\347\241\254\347\224\262\347\210\252\345\205\275.png" similarity index 100% rename from "images/\347\241\254\347\224\262\347\210\252\345\205\275.png" rename to "src/resources/assets/images/\347\241\254\347\224\262\347\210\252\345\205\275.png" diff --git "a/images/\347\251\272\351\231\215\345\205\265.png" "b/src/resources/assets/images/\347\251\272\351\231\215\345\205\265.png" similarity index 100% rename from "images/\347\251\272\351\231\215\345\205\265.png" rename to "src/resources/assets/images/\347\251\272\351\231\215\345\205\265.png" diff --git "a/images/\347\273\210\346\233\262\345\220\210\345\243\260.png" "b/src/resources/assets/images/\347\273\210\346\233\262\345\220\210\345\243\260.png" similarity index 100% rename from "images/\347\273\210\346\233\262\345\220\210\345\243\260.png" rename to "src/resources/assets/images/\347\273\210\346\233\262\345\220\210\345\243\260.png" diff --git "a/images/\350\205\220\350\264\245\351\252\221\345\243\253.png" "b/src/resources/assets/images/\350\205\220\350\264\245\351\252\221\345\243\253.png" similarity index 100% rename from "images/\350\205\220\350\264\245\351\252\221\345\243\253.png" rename to "src/resources/assets/images/\350\205\220\350\264\245\351\252\221\345\243\253.png" diff --git "a/images/\350\207\252\347\224\261\344\275\243\345\205\265\342\200\234\344\270\273\351\237\263\345\220\211\344\273\226\342\200\235.png" "b/src/resources/assets/images/\350\207\252\347\224\261\344\275\243\345\205\265\342\200\234\344\270\273\351\237\263\345\220\211\344\273\226\342\200\235.png" similarity index 100% rename from "images/\350\207\252\347\224\261\344\275\243\345\205\265\342\200\234\344\270\273\351\237\263\345\220\211\344\273\226\342\200\235.png" rename to "src/resources/assets/images/\350\207\252\347\224\261\344\275\243\345\205\265\342\200\234\344\270\273\351\237\263\345\220\211\344\273\226\342\200\235.png" diff --git "a/images/\350\207\252\347\224\261\344\275\243\345\205\265\342\200\234\350\264\235\346\226\257\342\200\235.png" "b/src/resources/assets/images/\350\207\252\347\224\261\344\275\243\345\205\265\342\200\234\350\264\235\346\226\257\342\200\235.png" similarity index 100% rename from "images/\350\207\252\347\224\261\344\275\243\345\205\265\342\200\234\350\264\235\346\226\257\342\200\235.png" rename to "src/resources/assets/images/\350\207\252\347\224\261\344\275\243\345\205\265\342\200\234\350\264\235\346\226\257\342\200\235.png" diff --git "a/images/\350\215\222\345\216\237\345\212\253\346\216\240\350\200\205.png" "b/src/resources/assets/images/\350\215\222\345\216\237\345\212\253\346\216\240\350\200\205.png" similarity index 100% rename from "images/\350\215\222\345\216\237\345\212\253\346\216\240\350\200\205.png" rename to "src/resources/assets/images/\350\215\222\345\216\237\345\212\253\346\216\240\350\200\205.png" diff --git "a/images/\350\220\250\345\215\241\345\205\271\345\244\247\345\211\221\346\211\213.png" "b/src/resources/assets/images/\350\220\250\345\215\241\345\205\271\345\244\247\345\211\221\346\211\213.png" similarity index 100% rename from "images/\350\220\250\345\215\241\345\205\271\345\244\247\345\211\221\346\211\213.png" rename to "src/resources/assets/images/\350\220\250\345\215\241\345\205\271\345\244\247\345\211\221\346\211\213.png" diff --git "a/images/\350\220\250\345\215\241\345\205\271\345\256\277\344\270\273\345\215\253\345\267\242\347\231\276\345\244\253\351\225\277.png" "b/src/resources/assets/images/\350\220\250\345\215\241\345\205\271\345\256\277\344\270\273\345\215\253\345\267\242\347\231\276\345\244\253\351\225\277.png" similarity index 100% rename from "images/\350\220\250\345\215\241\345\205\271\345\256\277\344\270\273\345\215\253\345\267\242\347\231\276\345\244\253\351\225\277.png" rename to "src/resources/assets/images/\350\220\250\345\215\241\345\205\271\345\256\277\344\270\273\345\215\253\345\267\242\347\231\276\345\244\253\351\225\277.png" diff --git "a/images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\345\220\236\345\231\254\350\200\205.png" "b/src/resources/assets/images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\345\220\236\345\231\254\350\200\205.png" similarity index 100% rename from "images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\345\220\236\345\231\254\350\200\205.png" rename to "src/resources/assets/images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\345\220\236\345\231\254\350\200\205.png" diff --git "a/images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\345\256\277\345\215\253.png" "b/src/resources/assets/images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\345\256\277\345\215\253.png" similarity index 100% rename from "images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\345\256\277\345\215\253.png" rename to "src/resources/assets/images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\345\256\277\345\215\253.png" diff --git "a/images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\346\210\230\350\275\246.png" "b/src/resources/assets/images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\346\210\230\350\275\246.png" similarity index 100% rename from "images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\346\210\230\350\275\246.png" rename to "src/resources/assets/images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\346\210\230\350\275\246.png" diff --git "a/images/\350\257\241\346\240\270\351\233\206\345\205\273\350\200\205.png" "b/src/resources/assets/images/\350\257\241\346\240\270\351\233\206\345\205\273\350\200\205.png" similarity index 100% rename from "images/\350\257\241\346\240\270\351\233\206\345\205\273\350\200\205.png" rename to "src/resources/assets/images/\350\257\241\346\240\270\351\233\206\345\205\273\350\200\205.png" diff --git "a/images/\350\265\217\351\207\221\347\214\216\344\272\272\345\274\251\346\211\213.png" "b/src/resources/assets/images/\350\265\217\351\207\221\347\214\216\344\272\272\345\274\251\346\211\213.png" similarity index 100% rename from "images/\350\265\217\351\207\221\347\214\216\344\272\272\345\274\251\346\211\213.png" rename to "src/resources/assets/images/\350\265\217\351\207\221\347\214\216\344\272\272\345\274\251\346\211\213.png" diff --git "a/images/\350\265\217\351\207\221\347\214\216\344\272\272\346\211\260\344\271\261\350\200\205.png" "b/src/resources/assets/images/\350\265\217\351\207\221\347\214\216\344\272\272\346\211\260\344\271\261\350\200\205.png" similarity index 100% rename from "images/\350\265\217\351\207\221\347\214\216\344\272\272\346\211\260\344\271\261\350\200\205.png" rename to "src/resources/assets/images/\350\265\217\351\207\221\347\214\216\344\272\272\346\211\260\344\271\261\350\200\205.png" diff --git "a/images/\350\277\267\350\267\257\347\232\204\345\267\250\345\203\217.png" "b/src/resources/assets/images/\350\277\267\350\267\257\347\232\204\345\267\250\345\203\217.png" similarity index 100% rename from "images/\350\277\267\350\267\257\347\232\204\345\267\250\345\203\217.png" rename to "src/resources/assets/images/\350\277\267\350\267\257\347\232\204\345\267\250\345\203\217.png" diff --git "a/images/\351\205\270\346\266\262\346\272\220\347\237\263\350\231\253\302\267\316\261.png" "b/src/resources/assets/images/\351\205\270\346\266\262\346\272\220\347\237\263\350\231\253\302\267\316\261.png" similarity index 100% rename from "images/\351\205\270\346\266\262\346\272\220\347\237\263\350\231\253\302\267\316\261.png" rename to "src/resources/assets/images/\351\205\270\346\266\262\346\272\220\347\237\263\350\231\253\302\267\316\261.png" diff --git "a/images/\351\207\215\350\243\205\351\230\262\345\276\241\350\200\205.png" "b/src/resources/assets/images/\351\207\215\350\243\205\351\230\262\345\276\241\350\200\205.png" similarity index 100% rename from "images/\351\207\215\350\243\205\351\230\262\345\276\241\350\200\205.png" rename to "src/resources/assets/images/\351\207\215\350\243\205\351\230\262\345\276\241\350\200\205.png" diff --git "a/images/\351\230\265\345\234\260\345\207\273\344\272\272\346\211\213.png" "b/src/resources/assets/images/\351\230\265\345\234\260\345\207\273\344\272\272\346\211\213.png" similarity index 100% rename from "images/\351\230\265\345\234\260\345\207\273\344\272\272\346\211\213.png" rename to "src/resources/assets/images/\351\230\265\345\234\260\345\207\273\344\272\272\346\211\213.png" diff --git "a/images/\351\243\216\346\203\205\350\241\227\342\200\234\346\230\237\346\234\257\345\270\210\342\200\235.png" "b/src/resources/assets/images/\351\243\216\346\203\205\350\241\227\342\200\234\346\230\237\346\234\257\345\270\210\342\200\235.png" similarity index 100% rename from "images/\351\243\216\346\203\205\350\241\227\342\200\234\346\230\237\346\234\257\345\270\210\342\200\235.png" rename to "src/resources/assets/images/\351\243\216\346\203\205\350\241\227\342\200\234\346\230\237\346\234\257\345\270\210\342\200\235.png" diff --git "a/images/\351\253\230\345\241\224\346\234\257\345\270\210.png" "b/src/resources/assets/images/\351\253\230\345\241\224\346\234\257\345\270\210.png" similarity index 100% rename from "images/\351\253\230\345\241\224\346\234\257\345\270\210.png" rename to "src/resources/assets/images/\351\253\230\345\241\224\346\234\257\345\270\210.png" diff --git "a/images/\351\253\230\346\231\256\345\260\274\345\205\213.png" "b/src/resources/assets/images/\351\253\230\346\231\256\345\260\274\345\205\213.png" similarity index 100% rename from "images/\351\253\230\346\231\256\345\260\274\345\205\213.png" rename to "src/resources/assets/images/\351\253\230\346\231\256\345\260\274\345\205\213.png" diff --git "a/images/\351\253\230\347\272\247\346\255\246\350\243\205\344\272\272\345\221\230.png" "b/src/resources/assets/images/\351\253\230\347\272\247\346\255\246\350\243\205\344\272\272\345\221\230.png" similarity index 100% rename from "images/\351\253\230\347\272\247\346\255\246\350\243\205\344\272\272\345\221\230.png" rename to "src/resources/assets/images/\351\253\230\347\272\247\346\255\246\350\243\205\344\272\272\345\221\230.png" diff --git "a/images/\351\253\230\350\203\275\346\272\220\347\237\263\350\231\253.png" "b/src/resources/assets/images/\351\253\230\350\203\275\346\272\220\347\237\263\350\231\253.png" similarity index 100% rename from "images/\351\253\230\350\203\275\346\272\220\347\237\263\350\231\253.png" rename to "src/resources/assets/images/\351\253\230\350\203\275\346\272\220\347\237\263\350\231\253.png" diff --git "a/images/\351\273\221\346\260\264\346\272\220\347\237\263\350\231\253.png" "b/src/resources/assets/images/\351\273\221\346\260\264\346\272\220\347\237\263\350\231\253.png" similarity index 100% rename from "images/\351\273\221\346\260\264\346\272\220\347\237\263\350\231\253.png" rename to "src/resources/assets/images/\351\273\221\346\260\264\346\272\220\347\237\263\350\231\253.png" diff --git a/src/resources/config/maa_option.json b/src/resources/config/maa_option.json new file mode 100644 index 0000000..5de39ef --- /dev/null +++ b/src/resources/config/maa_option.json @@ -0,0 +1,7 @@ +{ + "draw_quality": 85, + "logging": true, + "save_draw": false, + "save_on_error": true, + "stdout_level": 2 +} \ No newline at end of file diff --git a/monster.csv b/src/resources/data/monster.csv similarity index 100% rename from monster.csv rename to src/resources/data/monster.csv diff --git a/monster_greenvine.csv b/src/resources/data/monster_greenvine.csv similarity index 100% rename from monster_greenvine.csv rename to src/resources/data/monster_greenvine.csv diff --git a/simulator/README.md b/src/simulation/README.md similarity index 100% rename from simulator/README.md rename to src/simulation/README.md diff --git a/src/simulation/__init__.py b/src/simulation/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/simulator/arknights.csv b/src/simulation/arknights.csv similarity index 100% rename from simulator/arknights.csv rename to src/simulation/arknights.csv diff --git a/simulator/battle_field.py b/src/simulation/battle_field.py similarity index 60% rename from simulator/battle_field.py rename to src/simulation/battle_field.py index 7ba4abd..81b7828 100644 --- a/simulator/battle_field.py +++ b/src/simulation/battle_field.py @@ -1,9 +1,6 @@ -import json -import math import random import time import numpy as np -from enum import Enum import logging from typing import TYPE_CHECKING @@ -14,9 +11,13 @@ if TYPE_CHECKING: from .monsters import Monster - + from .monsters import MonsterFactory -from .utils import VIRTUAL_TIME_DELTA, BuffEffect, BuffType, Faction, SpatialHash +from .utils import ( + VIRTUAL_TIME_DELTA, + Faction, + SpatialHash, +) from .zone import PoisonZone # 场景参数 @@ -24,14 +25,14 @@ SPAWN_AREA = 2 # 阵营出生区域宽度 -from collections import defaultdict from .projectiles import ProjectileManager + class Battlefield: def __init__(self, monster_data): - self.monsters : list[Monster] = [] - self.alive_monsters : list[Monster] = [] - self.hash_grid : SpatialHash = SpatialHash(self, cell_size=0.5) + self.monsters: list[Monster] = [] + self.alive_monsters: list[Monster] = [] + self.hash_grid: SpatialHash = SpatialHash(self, cell_size=0.5) self.HIT_BOX_RADIUS = 0.2 self.round = 0 @@ -51,89 +52,105 @@ def __init__(self, monster_data): self.current_spawn_left = 0 self.current_spawn_right = 0 - def query_monster(self, target_position, radius) -> list['Monster']: + def query_monster(self, target_position, radius) -> list["Monster"]: results = [] if len(self.alive_monsters) < (radius / self.hash_grid.cell_size) ** 2: for m in self.alive_monsters: - if m.is_alive and (m.position - target_position).magnitude <= radius: + if ( + m.is_alive + and (m.position - target_position).magnitude <= radius + ): results.append(m) else: - for id in self.hash_grid.query_neighbors(target_position, radius): - m = self.get_monster_with_id(id) - if m.is_alive and (m.position - target_position).magnitude <= radius: + for monster_id in self.hash_grid.query_neighbors( + target_position, radius + ): + m = self.get_monster_with_id(monster_id) + if ( + m.is_alive + and (m.position - target_position).magnitude <= radius + ): results.append(m) return results - def append_monster(self, monster : 'Monster'): + def append_monster(self, monster: "Monster"): """添加一个怪物到战场""" - id = self.globalId - monster.id = id + monster_id = self.globalId + monster.id = monster_id self.globalId += 1 self.monsters.append(monster) self.hash_grid.insert(monster.position, monster.id) - - def append_monster_name(self, name, faction, pos) -> 'Monster': + + def append_monster_name(self, name, faction, pos) -> "Monster": """添加一个怪物到战场,只需要名字""" data = next((m for m in self.monster_data if m["名字"] == name), None) - id = self.globalId + monster_id = self.globalId monster = MonsterFactory.create_monster(data, faction, pos, self) - monster.id = id + monster.id = monster_id self.globalId += 1 self.monsters.append(monster) self.hash_grid.insert(monster.position, monster.id) return monster - def get_monster_with_id(self, id) -> 'Monster': - return self.monsters[id] - + def get_monster_with_id(self, monster_id) -> "Monster": + return self.monsters[monster_id] + def setup_battle(self, left_army, right_army, monster_data): """二维战场初始化""" # 左阵营生成在左上区域 - for (name, count) in left_army.items(): + for name, count in left_army.items(): data = next((m for m in monster_data if m["名字"] == name), None) if data is None: raise ValueError(f"左侧怪物 {name} 在 monster_data 中未找到!") for _ in range(count): pos = FastVector( - random.uniform(0, 0.5), - random.uniform(0, MAP_SIZE[1]) + random.uniform(0, 0.5), random.uniform(0, MAP_SIZE[1]) + ) + self.monster_temporal_area_left.append( + MonsterFactory.create_monster( + data, Faction.LEFT, pos, self + ) ) - self.monster_temporal_area_left.append( MonsterFactory.create_monster(data, Faction.LEFT, pos, self)) # 右阵营生成在右下区域 - for (name, count) in right_army.items(): + for name, count in right_army.items(): data = next((m for m in monster_data if m["名字"] == name), None) if data is None: raise ValueError(f"右侧怪物 {name} 在 monster_data 中未找到!") for _ in range(count): pos = FastVector( - random.uniform(MAP_SIZE[0]-0.5, MAP_SIZE[0]), - random.uniform(0, MAP_SIZE[1]) + random.uniform(MAP_SIZE[0] - 0.5, MAP_SIZE[0]), + random.uniform(0, MAP_SIZE[1]), + ) + self.monster_temporal_area_right.append( + MonsterFactory.create_monster( + data, Faction.RIGHT, pos, self + ) ) - self.monster_temporal_area_right.append(MonsterFactory.create_monster(data, Faction.RIGHT, pos, self)) self.alive_monsters = self.monsters self.gameTime = 0 - self.current_spawn = 0 random.shuffle(self.monster_temporal_area_left) random.shuffle(self.monster_temporal_area_right) return True def check_victory(self): """检查胜利条件""" - if self.current_spawn_left < len(self.monster_temporal_area_left) or self.current_spawn_right < len(self.monster_temporal_area_right): + if self.current_spawn_left < len( + self.monster_temporal_area_left + ) or self.current_spawn_right < len(self.monster_temporal_area_right): return None alive_factions = set() for m in self.alive_monsters: if m.is_alive: alive_factions.add(m.faction) - + if len(alive_factions) == 1: return list(alive_factions)[0] elif len(alive_factions) == 0: return Faction.LEFT return None - + def check_zone(self): new_zone = [] # 检查场地效果 @@ -151,12 +168,23 @@ def run_one_frame(self): self.round += 1 if self.round < 40 or self.round > 90: - if self.current_spawn_left < len(self.monster_temporal_area_left) and self.round % 2 == 0: - self.append_monster(self.monster_temporal_area_left[self.current_spawn_left]) + if ( + self.current_spawn_left < len(self.monster_temporal_area_left) + and self.round % 2 == 0 + ): + self.append_monster( + self.monster_temporal_area_left[self.current_spawn_left] + ) self.current_spawn_left += 1 - if self.current_spawn_right < len(self.monster_temporal_area_right) and self.round % 2 == 0: - self.append_monster(self.monster_temporal_area_right[self.current_spawn_right]) + if ( + self.current_spawn_right + < len(self.monster_temporal_area_right) + and self.round % 2 == 0 + ): + self.append_monster( + self.monster_temporal_area_right[self.current_spawn_right] + ) self.current_spawn_right += 1 self.check_zone() @@ -172,21 +200,27 @@ def run_one_frame(self): self.alive_monsters = [m for m in self.monsters if m.is_alive] winner = self.check_victory() if winner: - logger.info(f"\nVictory for {winner.name}!") - left = len([m for m in self.alive_monsters if m.is_alive and m.faction == Faction.LEFT]) - logger.info(f"左边存活{left} / 右边存活{len(self.alive_monsters) - left}") + logger.info("\nVictory for %s!", winner.name) + left = len( + [ + m + for m in self.alive_monsters + if m.is_alive and m.faction == Faction.LEFT + ] + ) + logger.info("左边存活%s / 右边存活%s", left, len(self.alive_monsters) - left) return winner - + self.gameTime += VIRTUAL_TIME_DELTA return None - + def run_battle(self, visualize=False): """运行战斗直到决出胜负""" while True: if visualize and self.round % 30 == 0: self.print_battlefield() time.sleep(1) - + result = self.run_one_frame() if result != None: return result @@ -195,29 +229,33 @@ def danger_zone_size(self): if self.gameTime < 40: return 0 return int((self.gameTime - 40) / 20) + 1 - + def add_new_zone(self, zone): self.effect_zones.append(zone) def print_battlefield(self): """二维战场可视化""" - grid = np.full((MAP_SIZE[1] * 2, MAP_SIZE[0] * 2), '.', dtype='U2') - + grid = np.full((MAP_SIZE[1] * 2, MAP_SIZE[0] * 2), ".", dtype="U2") + for m in self.alive_monsters: if m.is_alive: - x = np.minimum(np.maximum(0, int(m.position.x * 2)), MAP_SIZE[0]*2-1) - y = np.minimum(np.maximum(0, int(m.position.y * 2)), MAP_SIZE[1]*2-1) - symbol = 'L' if m.faction == Faction.LEFT else 'R' - if grid[y, x] != '.' and symbol != grid[y, x]: - symbol = 'X' + x = np.minimum( + np.maximum(0, int(m.position.x * 2)), MAP_SIZE[0] * 2 - 1 + ) + y = np.minimum( + np.maximum(0, int(m.position.y * 2)), MAP_SIZE[1] * 2 - 1 + ) + symbol = "L" if m.faction == Faction.LEFT else "R" + if grid[y, x] != "." and symbol != grid[y, x]: + symbol = "X" if m.char_icon != "": symbol = m.char_icon grid[y, x] = symbol - - logger.info(f"\nRound {self.round}") + + logger.info("\nRound %s", self.round) for row in grid: - logger.info(' '.join(row)) + logger.info(" ".join(row)) def get_grid(self, target): x, y = int(target.position.x), int(target.position.y) - return x, y \ No newline at end of file + return x, y diff --git a/simulator/elemental.py b/src/simulation/elemental.py similarity index 82% rename from simulator/elemental.py rename to src/simulation/elemental.py index 41a0c90..2e00207 100644 --- a/simulator/elemental.py +++ b/src/simulation/elemental.py @@ -1,13 +1,9 @@ -from enum import Enum -import math - -import numpy as np - from .utils import DamageType, ElementType, debug_print, lerp class ElementAccumulator: """多元素损伤容器""" + def __init__(self, owner): self.accumulators = {et: 0.0 for et in ElementType} self.active_burst = None @@ -18,15 +14,17 @@ def accumulate(self, element: ElementType, value: float): """累积元素损伤""" if self.active_burst: return # 爆条期间暂停累积 - + self.accumulators[element] += value limits = 2000 if self.owner.boss else 1000 if self.accumulators[element] >= limits: self.accumulators[element] = 0 self.active_burst = ElementBurst(element, self.owner) + class ElementBurst: """爆条效果控制器""" + def __init__(self, trigger_element: ElementType, owner): self.owner = owner self.start_time = owner.battlefield.gameTime @@ -51,14 +49,20 @@ def _init_effect_params(self): elif self.trigger_element == ElementType.FIRE: self.duration = 10 dmg = 7000 - debug_print(f"{self.owner.name}{self.owner.id} 灼燃发期间受到{dmg}点伤害") + debug_print( + f"{self.owner.name}{self.owner.id} 灼燃发期间受到{dmg}点伤害" + ) self.owner.take_damage(dmg, DamageType.TRUE) self.owner.magic_resist -= 20 @property def progress(self): """效果进度百分比""" - return min(1.0, (self.owner.battlefield.gameTime - self.start_time) / self.duration) + return min( + 1.0, + (self.owner.battlefield.gameTime - self.start_time) + / self.duration, + ) def on_clear(self): if self.trigger_element == ElementType.FIRE: @@ -69,19 +73,27 @@ def update_effect(self, deltaTime): if self.trigger_element == ElementType.NECRO_RIGHT: # 虚弱效果衰减 self.owner.attack_multiplier = lerp(0.5, 1, self.progress) - + # 持续伤害应用 self.dot_timer += deltaTime - self.owner.take_damage(self.dot_damage * deltaTime, DamageType.TRUE) + self.owner.take_damage( + self.dot_damage * deltaTime, DamageType.TRUE + ) if self.dot_timer >= 1.0: - debug_print(f"{self.owner.name}{self.owner.id} 凋亡损伤爆发期间受到{self.dot_damage}伤害") + debug_print( + f"{self.owner.name}{self.owner.id} 凋亡损伤爆发期间受到{self.dot_damage}伤害" + ) self.dot_timer = 0 elif self.trigger_element == ElementType.NECRO_LEFT: # 持续伤害应用 self.dot_timer += deltaTime - self.owner.take_damage(self.dot_damage * deltaTime, DamageType.TRUE) + self.owner.take_damage( + self.dot_damage * deltaTime, DamageType.TRUE + ) if self.dot_timer >= 1.0: - debug_print(f"{self.owner.name}{self.owner.id} 凋亡损伤爆发期间受到{self.dot_damage}伤害") + debug_print( + f"{self.owner.name}{self.owner.id} 凋亡损伤爆发期间受到{self.dot_damage}伤害" + ) self.dot_timer = 0 @@ -91,25 +103,25 @@ def update_effect(self, deltaTime): # self.resistances = {et: 0.0 for et in ElementType} # self.active_effects = [] # self.hp = max_hp - + # def take_element_damage(self, element: ElementType, base_damage: float): # # 计算实际伤害(考虑抗性) # resistance = self.resistances.get(element, 0) # actual_damage = base_damage * (1 - resistance) # self.hp -= actual_damage - + # # 累积30%基础伤害作为元素损伤 # self.element_system.accumulate(element, base_damage * 0.3) - + # def set_element_resistance(self, element: ElementType, value: float): # """设置元素抗性(0.0-1.0)""" # self.resistances[element] = max(0, min(1.0, value)) - + # def update(self): # """每帧更新状态""" # # 处理爆条队列 # self.element_system.process_burst() - + # # 更新激活中的爆条效果 # if burst := self.element_system.active_burst: # if time.time() - burst.start_time > burst.duration: @@ -121,4 +133,4 @@ def update_effect(self, deltaTime): # """清除爆条残留效果""" # self.element_system.active_burst = None # for elem in ElementType: -# self.resistances[elem] = 0.0 \ No newline at end of file +# self.resistances[elem] = 0.0 diff --git a/simulator/monsters.json b/src/simulation/monsters.json similarity index 100% rename from simulator/monsters.json rename to src/simulation/monsters.json diff --git a/simulator/monsters.py b/src/simulation/monsters.py similarity index 80% rename from simulator/monsters.py rename to src/simulation/monsters.py index d9a1122..7878046 100644 --- a/simulator/monsters.py +++ b/src/simulation/monsters.py @@ -1,8 +1,5 @@ -from dataclasses import dataclass, field -import json import math import random -import time from enum import Enum from typing import List import numpy as np @@ -14,10 +11,16 @@ from .projectiles import AOEType, AOE炸弹, AOE炸弹锁定 if TYPE_CHECKING: - from battle_field import Battlefield + from .battle_field import Battlefield from .elemental import ElementAccumulator, ElementType -from .utils import BuffEffect, BuffType, DamageType, calculate_normal_dmg, debug_print, Faction +from .utils import ( + BuffEffect, + BuffType, + DamageType, + calculate_normal_dmg, + debug_print, +) from .zone import WineZone @@ -28,7 +31,7 @@ class AttackState(Enum): class AttackAnimation: - def __init__(self, 前摇时间, 后摇时间, 等待时间, monster: 'Monster'): + def __init__(self, 前摇时间, 后摇时间, 等待时间, monster: "Monster"): self.前摇时间 = 前摇时间 self.后摇时间 = 后摇时间 self.等待时间 = 等待时间 @@ -49,7 +52,13 @@ def idle_time(self): class TargetSelector: @staticmethod - def select_targets(attacker, battlefield, need_in_range=False, max_targets=2, reverse=False): + def select_targets( + attacker, + battlefield, + need_in_range=False, + max_targets=2, + reverse=False, + ): """ 带嘲讽等级的目标选择算法 优先级: 攻击范围内最高嘲讽等级 > 同等级最近目标 > 全局最近目标 @@ -61,9 +70,11 @@ def select_targets(attacker, battlefield, need_in_range=False, max_targets=2, re # if m.can_be_target() # and m.faction != attacker.faction] # else: - enemies: list[Monster] = [m for m in battlefield.alive_monsters - if m.can_be_target() - and m.faction != attacker.faction] + enemies: list[Monster] = [ + m + for m in battlefield.alive_monsters + if m.can_be_target() and m.faction != attacker.faction + ] # battlefield.query_monster(attacker.position, attacker.attack_range if need_in_range else 9999) # enemies = if not enemies: @@ -76,34 +87,40 @@ def select_targets(attacker, battlefield, need_in_range=False, max_targets=2, re in_range = dist <= attacker.attack_range if not need_in_range or (need_in_range and in_range): - enemy_info.append({ - "enemy": enemy, - "distance": dist, - "aggro": enemy.aggro if in_range else 0 - }) + enemy_info.append( + { + "enemy": enemy, + "distance": dist, + "aggro": enemy.aggro if in_range else 0, + } + ) # 按照优先级排序:嘲讽降序 -> 距离升序 if reverse: - sorted_enemies = sorted(enemy_info, - key=lambda x: (-x["distance"])) + sorted_enemies = sorted(enemy_info, key=lambda x: (-x["distance"])) else: - sorted_enemies = sorted(enemy_info, - key=lambda x: (-x["aggro"], x["distance"])) + sorted_enemies = sorted( + enemy_info, key=lambda x: (-x["aggro"], x["distance"]) + ) count = np.minimum(max_targets, len(sorted_enemies)) # 选择前N个目标 return [e["enemy"] for e in sorted_enemies[:count]] @staticmethod - def select_targets_lowest_health(attacker, battlefield, need_in_range=False, max_targets=2): + def select_targets_lowest_health( + attacker, battlefield, need_in_range=False, max_targets=2 + ): """ 带嘲讽等级的目标选择算法 优先级: 攻击范围内最高嘲讽等级 > 同等级最近目标 > 全局最近目标 """ # 获取所有有效敌人 - enemies: list[Monster] = [m for m in battlefield.alive_monsters - if m.can_be_target() - and m.faction != attacker.faction] + enemies: list[Monster] = [ + m + for m in battlefield.alive_monsters + if m.can_be_target() and m.faction != attacker.faction + ] if not enemies: return [] @@ -115,16 +132,20 @@ def select_targets_lowest_health(attacker, battlefield, need_in_range=False, max in_range = dist <= attacker.attack_range if not need_in_range or (need_in_range and in_range): - enemy_info.append({ - "enemy": enemy, - "distance": dist, - "aggro": enemy.aggro if in_range else 0, - "health_ratio": enemy.health / enemy.max_health - }) + enemy_info.append( + { + "enemy": enemy, + "distance": dist, + "aggro": enemy.aggro if in_range else 0, + "health_ratio": enemy.health / enemy.max_health, + } + ) # 按照优先级排序:嘲讽降序 -> 距离升序 - sorted_enemies = sorted(enemy_info, - key=lambda x: (x["health_ratio"], -x["aggro"], x["distance"])) + sorted_enemies = sorted( + enemy_info, + key=lambda x: (x["health_ratio"], -x["aggro"], x["distance"]), + ) count = np.minimum(max_targets, len(sorted_enemies)) # 选择前N个目标 @@ -145,7 +166,9 @@ def apply(self, effect): if effect.type in self.owner.immunity: return # 处理效果叠加逻辑 - existing = next((e for e in self.effects if e.type == effect.type), None) + existing = next( + (e for e in self.effects if e.type == effect.type), None + ) # # 已经冰冻住了就不要施加寒冷效果了 # if effect.type == BuffType.CHILL: @@ -156,8 +179,18 @@ def apply(self, effect): # 寒冷效果叠加就会变成冰冻 if effect.type == BuffType.CHILL: existing.duration = 0 - self.apply(BuffEffect(BuffType.FROZEN, effect.duration, effect.source, effect.stacks, effect.data)) - debug_print(f"{self.owner.name}{self.owner.id} 被 {effect.source.name} 冰冻了!") + self.apply( + BuffEffect( + BuffType.FROZEN, + effect.duration, + effect.source, + effect.stacks, + effect.data, + ) + ) + debug_print( + f"{self.owner.name}{self.owner.id} 被 {effect.source.name} 冰冻了!" + ) return # 其他效果刷新时间 @@ -194,25 +227,38 @@ def _process_dot(self, delta_time): # self.fire_dmg_counter += delta_time # if self.fire_dmg_counter >= 0.33: # self.fire_dmg_counter = 0 - damage = calculate_normal_dmg(0, self.owner.magic_resist, 60 * delta_time, DamageType.MAGIC) + damage = calculate_normal_dmg( + 0, self.owner.magic_resist, 60 * delta_time, DamageType.MAGIC + ) self.owner.take_damage(damage, DamageType.MAGIC) - corrupt = next((e for e in self.effects if e.type == BuffType.CORRUPT), None) + corrupt = next( + (e for e in self.effects if e.type == BuffType.CORRUPT), None + ) if corrupt: # 每秒造成伤害 # self.corrupt_dmg_counter += delta_time # if self.corrupt_dmg_counter >= 1: # self.corrupt_dmg_counter = 0 - damage = calculate_normal_dmg(0, self.owner.magic_resist, 100 * delta_time, DamageType.MAGIC) + damage = calculate_normal_dmg( + 0, self.owner.magic_resist, 100 * delta_time, DamageType.MAGIC + ) self.owner.take_damage(damage, DamageType.MAGIC) - power_stone = next((e for e in self.effects if e.type == BuffType.POWER_STONE), None) + power_stone = next( + (e for e in self.effects if e.type == BuffType.POWER_STONE), None + ) if power_stone: self.power_stay_counter += delta_time - damage = 0.005 * self.owner.max_health * self.power_stay_counter * delta_time + damage = ( + 0.005 + * self.owner.max_health + * self.power_stay_counter + * delta_time + ) if self.owner.take_damage(damage, DamageType.TRUE): pass - #debug_print(f"{self.owner.name}{self.owner.id} 受到了毒圈的{damage}伤害") + # debug_print(f"{self.owner.name}{self.owner.id} 受到了毒圈的{damage}伤害") def _init_effect(self, effect): """初始化效果""" @@ -285,7 +331,9 @@ def __init__(self, data, faction, position, battlefield): self.attack_range = data["攻击范围"]["数值"] self.move_speed = data["移速"]["数值"] self.traits = data["特性"] - self.attack_type = DamageType.PHYSICAL if data["类型"] == "物理" else DamageType.MAGIC + self.attack_type = ( + DamageType.PHYSICAL if data["类型"] == "物理" else DamageType.MAGIC + ) self.char_icon = data.get("符号", "") self.id = -1 self.attack_speed = 100 @@ -301,7 +349,7 @@ def __init__(self, data, faction, position, battlefield): self.frozen = False self.dizzy = False self.invincible = False - self.battlefield: 'Battlefield' = battlefield + self.battlefield: "Battlefield" = battlefield self.status_system = StatusSystem(self) self.element_system = ElementAccumulator(self) self.attack_multiplier = 1 @@ -360,7 +408,9 @@ def increase_skill_cd(self, delta_time): def increase_attack_cd(self, delta_time): """增加攻击技力、攻击频率计算""" - self.attack_time_counter += delta_time * (np.maximum(10, np.minimum(self.attack_speed, 600)) / 100) + self.attack_time_counter += delta_time * ( + np.maximum(10, np.minimum(self.attack_speed, 600)) / 100 + ) def move_toward_enemy(self, delta_time): """根据阵营向对方移动""" @@ -379,7 +429,9 @@ def move_toward_enemy(self, delta_time): # 标准化移动向量并应用速度 norm_direction = direction.normalize() if not self.blocked and self.attack_state == AttackState.等待: - self.velocity = (self.velocity * 7 + norm_direction * self.move_speed) / 8 + self.velocity = ( + self.velocity * 7 + norm_direction * self.move_speed + ) / 8 RADIUS = self.battlefield.HIT_BOX_RADIUS selfRadius = RADIUS * 0.2 if self.blocked else RADIUS @@ -403,7 +455,12 @@ def move_toward_enemy(self, delta_time): depth = selfRadius + radius2 - dist if dist < selfRadius + radius2: # 发生碰撞,挤出 - self.velocity -= dir * (depth + 0.02) * hardness1 / (hardness1 + hardness2) + self.velocity -= ( + dir + * (depth + 0.02) + * hardness1 + / (hardness1 + hardness2) + ) # m.velocity += dir * (depth + 0.02) * hardness2 / (hardness1 + hardness2) def do_move(self, delta_time): @@ -446,7 +503,10 @@ def can_attack(self, delta_time): if self.attack_state == AttackState.前摇: if in_range: self.increase_attack_cd(delta_time) - if self.attack_time_counter >= self.attack_animation.windup_time: + if ( + self.attack_time_counter + >= self.attack_animation.windup_time + ): self.attack_state = AttackState.后摇 return True else: @@ -480,8 +540,12 @@ def update(self, delta_time): self.status_system.update(delta_time) self.update_elemental(delta_time) - if self.target is None or not self.target.can_be_target() or ( - self.target.position - self.position).magnitude > self.attack_range: + if ( + self.target is None + or not self.target.can_be_target() + or (self.target.position - self.position).magnitude + > self.attack_range + ): # 寻找新目标 self.target = self.find_target() @@ -503,7 +567,9 @@ def update(self, delta_time): def find_target(self): """寻找最近的可攻击目标""" - targets = TargetSelector.select_targets(self, self.battlefield, need_in_range=False, max_targets=1) + targets = TargetSelector.select_targets( + self, self.battlefield, need_in_range=False, max_targets=1 + ) if len(targets) > 0: return targets[0] return None @@ -519,15 +585,21 @@ def attack(self, target, gameTime): target.on_hit(self, damage) def apply_damage_to_target(self, target, damage) -> bool: - debug_print(f"{self.name}{self.id} 对 {target.name}{target.id} 造成{damage}点{self.attack_type}伤害") + debug_print( + f"{self.name}{self.id} 对 {target.name}{target.id} 造成{damage}点{self.attack_type}伤害" + ) if target.take_damage(damage, self.attack_type): return True - debug_print(f"{self.name}{self.id} 没有对 {target.name}{target.id}造成伤害") + debug_print( + f"{self.name}{self.id} 没有对 {target.name}{target.id}造成伤害" + ) return False def calculate_damage(self, target, damage): """计算伤害值""" - return calculate_normal_dmg(target.phy_def, target.magic_resist, damage, self.attack_type) + return calculate_normal_dmg( + target.phy_def, target.magic_resist, damage, self.attack_type + ) # if self.attack_type == "物理": # return calculate_normal_dmg(target.phy_def, 0, damage, False) # # return np.maximum(damage - target.phy_def, int(damage * 0.05)) @@ -565,6 +637,7 @@ def on_attack(self, target, damage): target.phy_def = max(0, target.phy_def - 15) debug_print(f"{self.name} 使 {target.name} 防御力降低15") return True + # def apply_damage_to_target(self, target, damage): # if super().apply_damage_to_target(target, damage): # # 实现减防特性 @@ -583,8 +656,17 @@ def on_death(self): debug_print(f"{self.name} 即将自爆!") self.battlefield.projectiles_manager.spawn_projectile( - AOE炸弹(0.2, self.get_attack_power() * 4, DamageType.PHYSICAL, self, self.position, name="源石虫爆炸", - aoeType=AOEType.Circle, radius=1.25)) + AOE炸弹( + 0.2, + self.get_attack_power() * 4, + DamageType.PHYSICAL, + self, + self.position, + name="源石虫爆炸", + aoeType=AOEType.Circle, + radius=1.25, + ) + ) # for m in self.battlefield.monsters: # if m.faction != self.faction and m.is_alive: # distance = np.linalg.norm(m.position - self.position) @@ -600,7 +682,9 @@ class 炽焰源石虫(Monster): def apply_damage_to_target(self, target: Monster, damage): if super().apply_damage_to_target(target, damage): - target.element_system.accumulate(ElementType.FIRE, self.get_attack_power()) + target.element_system.accumulate( + ElementType.FIRE, self.get_attack_power() + ) return True return False @@ -620,9 +704,7 @@ def on_death(self): m.take_damage(dmg, self.attack_type) # 施加10秒寒冷效果 chill = BuffEffect( - type=BuffType.CHILL, - duration=10, - source=self + type=BuffType.CHILL, duration=10, source=self ) m.status_system.apply(chill) debug_print(f"{m.name} 受到{dmg}点爆炸伤害") @@ -639,11 +721,7 @@ def on_hit(self, attacker, damage): super().on_hit(attacker, damage) # 触发加速特性 if self.is_alive and self.speed_boost_counter <= 0: - speed = BuffEffect( - type=BuffType.SPEEDUP, - duration=2, - source=self - ) + speed = BuffEffect(type=BuffType.SPEEDUP, duration=2, source=self) self.status_system.apply(speed) self.speed_boost_counter = 7.0 debug_print(f"{self.name} 进入极速状态!") @@ -678,10 +756,14 @@ def on_death(self): def spawn_small(self): debug_print(f"{self.name} 释放小喷蛛") - self.battlefield.append_monster_name("小喷蛛", self.faction, self.position + FastVector( - random.uniform(-1, 1) * 0.2, - random.uniform(-1, 1) * 0.2 - )) + self.battlefield.append_monster_name( + "小喷蛛", + self.faction, + self.position + + FastVector( + random.uniform(-1, 1) * 0.2, random.uniform(-1, 1) * 0.2 + ), + ) class 提亚卡乌好战者(Monster): @@ -792,7 +874,10 @@ def attack(self, target, gameTime): debug_print(f"{self.name} 进入防御模式") def on_extra_update(self, delta_time): - if self.stage == 1 and self.battlefield.gameTime - self.last_attack_time >= 20.0: + if ( + self.stage == 1 + and self.battlefield.gameTime - self.last_attack_time >= 20.0 + ): self.phy_def -= 300 self.move_speed = self.original_speed self.defenseMode = False @@ -830,7 +915,9 @@ def calculate_damage(self, target, damage): target_def = target.phy_def if self.attack_count >= 4: target_def = target_def * 0.4 - return calculate_normal_dmg(target_def, target.magic_resist, damage, DamageType.PHYSICAL) + return calculate_normal_dmg( + target_def, target.magic_resist, damage, DamageType.PHYSICAL + ) class 高塔术师(Monster): @@ -840,22 +927,36 @@ def on_spawn(self): self.attack_animation = AttackAnimation(0.07, 0.13, 0.8, self) def attack(self, target, gameTime): - targets = TargetSelector.select_targets(self, self.battlefield, need_in_range=True, max_targets=2) + targets = TargetSelector.select_targets( + self, self.battlefield, need_in_range=True, max_targets=2 + ) if len(targets) == 0: return for t in targets: self.battlefield.projectiles_manager.spawn_projectile( - AOE炸弹锁定(0.1, self.get_attack_power(), DamageType.MAGIC, self, t, name="爆裂魔法", - aoeType=AOEType.Grid8)) + AOE炸弹锁定( + 0.1, + self.get_attack_power(), + DamageType.MAGIC, + self, + t, + name="爆裂魔法", + aoeType=AOEType.Grid8, + ) + ) debug_print(f"{self.name}{self.id} 射出爆裂魔法") def get_aoe_targets(self, target): - aoe_targets = [m for m in self.battlefield.monsters - if m.is_alive and m.faction != self.faction - and abs(m.position.x - target.position.x) <= 1 - and abs(m.position.y - target.position.y) <= 1] + aoe_targets = [ + m + for m in self.battlefield.monsters + if m.is_alive + and m.faction != self.faction + and abs(m.position.x - target.position.x) <= 1 + and abs(m.position.y - target.position.y) <= 1 + ] return aoe_targets @@ -884,16 +985,16 @@ def apply_damage_to_target(self, target, damage): if self.attack_count % 3 == 0: # 施加寒冷效果 chill = BuffEffect( - type=BuffType.CHILL, - duration=5, - source=self + type=BuffType.CHILL, duration=5, source=self ) target.status_system.apply(chill) return True return False def attack(self, target, gameTime): - targets = TargetSelector.select_targets(self, self.battlefield, need_in_range=True, max_targets=2) + targets = TargetSelector.select_targets( + self, self.battlefield, need_in_range=True, max_targets=2 + ) if len(targets) == 0: return @@ -916,7 +1017,9 @@ def on_hit(self, attacker, damage): damage = self.calculate_damage(attacker, 300) if self.apply_damage_to_target(attacker, damage): attacker.on_hit(self, damage) - debug_print(f"{self.name}{self.id} 对 {attacker.name}{attacker.id} 造成{damage}伤害") + debug_print( + f"{self.name}{self.id} 对 {attacker.name}{attacker.id} 造成{damage}伤害" + ) class 庞贝(Monster): @@ -937,8 +1040,9 @@ def get_max_skill_bar(self): return 10 def attack(self, target, gameTime): - targets: list[Monster] = TargetSelector.select_targets(self, self.battlefield, need_in_range=True, - max_targets=4) + targets: list[Monster] = TargetSelector.select_targets( + self, self.battlefield, need_in_range=True, max_targets=4 + ) if len(targets) == 0: return @@ -946,11 +1050,9 @@ def attack(self, target, gameTime): damage = self.calculate_damage(m, self.get_attack_power()) if self.apply_damage_to_target(m, damage): m.on_hit(self, damage) - m.status_system.apply(BuffEffect( - type=BuffType.FIRE, - duration=10, - source=self - )) + m.status_system.apply( + BuffEffect(type=BuffType.FIRE, duration=10, source=self) + ) def on_extra_update(self, delta_time): if not self.rage_mode and self.health < 0.5 * self.max_health: @@ -958,10 +1060,19 @@ def on_extra_update(self, delta_time): self.attack_speed += 40 debug_print(f"{self.name} 进入狂暴模式") self.ring_attack_counter += delta_time - targets = TargetSelector.select_targets(self, self.battlefield, need_in_range=False, max_targets=9999) - if len(targets) > 0 and (targets[0].position - self.position).magnitude < 0.8: + targets = TargetSelector.select_targets( + self, self.battlefield, need_in_range=False, max_targets=9999 + ) + if ( + len(targets) > 0 + and (targets[0].position - self.position).magnitude < 0.8 + ): if self.ring_attack_counter >= 10.0: - targets = [t for t in targets if (t.position - self.position).magnitude < 1.4] + targets = [ + t + for t in targets + if (t.position - self.position).magnitude < 1.4 + ] for tar in targets: dmg = self.calculate_damage(tar, 1000) if self.apply_damage_to_target(tar, dmg): @@ -973,11 +1084,9 @@ class 食腐狗(Monster): """食腐狗""" def on_attack(self, target, damage): - target.status_system.apply(BuffEffect( - type=BuffType.CORRUPT, - duration=10, - source=self - )) + target.status_system.apply( + BuffEffect(type=BuffType.CORRUPT, duration=10, source=self) + ) class 鼠鼠(Monster): @@ -990,11 +1099,7 @@ def on_hit(self, attacker, damage): super().on_hit(attacker, damage) # 触发加速特性 if self.is_alive and self.speed_boost_counter <= 0: - speed = BuffEffect( - type=BuffType.SPEEDUP, - duration=5, - source=self - ) + speed = BuffEffect(type=BuffType.SPEEDUP, duration=5, source=self) self.status_system.apply(speed) self.speed_boost_counter = 15.0 debug_print(f"{self.name}{self.id} 进入极速状态!") @@ -1013,14 +1118,23 @@ def on_spawn(self): def on_extra_update(self, delta_time): if self.first_attack: - targets: list[Monster] = TargetSelector.select_targets(self, self.battlefield, need_in_range=True, - max_targets=1) + targets: list[Monster] = TargetSelector.select_targets( + self, self.battlefield, need_in_range=True, max_targets=1 + ) if len(targets) == 0: return self.battlefield.projectiles_manager.spawn_projectile( - AOE炸弹锁定(0.2, self.get_attack_power() * 1.5, DamageType.MAGIC, self, targets[0], name="“投石机”", - aoeType=AOEType.Grid4)) + AOE炸弹锁定( + 0.2, + self.get_attack_power() * 1.5, + DamageType.MAGIC, + self, + targets[0], + name="“投石机”", + aoeType=AOEType.Grid4, + ) + ) self.attack_range = 0.8 self.first_attack = False debug_print(f"{self.name}{self.id} 投掷雪球") @@ -1049,12 +1163,12 @@ def apply_damage_to_target(self, target, damage): # 每第四下攻击会眩晕对面7秒 if self.attack_count % 4 == 0: dizzy = BuffEffect( - type=BuffType.DIZZY, - duration=7, - source=self + type=BuffType.DIZZY, duration=7, source=self ) target.status_system.apply(dizzy) - debug_print(f"{self.name}{self.id} 眩晕了 {target.name}{target.id}") + debug_print( + f"{self.name}{self.id} 眩晕了 {target.name}{target.id}" + ) return True return False @@ -1084,8 +1198,9 @@ def attack(self, target, gameTime): if self.stage == 0: self.on_attack(target, 0) if self.attack_count % 4 == 0: - targets: list[Monster] = TargetSelector.select_targets(self, self.battlefield, need_in_range=True, - max_targets=2) + targets: list[Monster] = TargetSelector.select_targets( + self, self.battlefield, need_in_range=True, max_targets=2 + ) if len(targets) == 0: return @@ -1093,11 +1208,11 @@ def attack(self, target, gameTime): damage = self.calculate_damage(m, self.get_attack_power()) if self.apply_damage_to_target(m, damage): m.on_hit(self, damage) - m.status_system.apply(BuffEffect( - type=BuffType.DIZZY, - duration=3, - source=self - )) + m.status_system.apply( + BuffEffect( + type=BuffType.DIZZY, duration=3, source=self + ) + ) else: super().attack(target, gameTime) return @@ -1114,7 +1229,9 @@ def attack(self, target, gameTime): def calculate_damage(self, target: Monster, damage): if self.stage == 1 and self.attack_count % 4 == 0: target_def = target.phy_def * 0.4 - return calculate_normal_dmg(target_def, target.magic_resist, damage, DamageType.PHYSICAL) + return calculate_normal_dmg( + target_def, target.magic_resist, damage, DamageType.PHYSICAL + ) return super().calculate_damage(target, damage) def on_death(self): @@ -1134,15 +1251,9 @@ def on_death(self): self.health = self.max_health self.status_system.reset() switch_stage = BuffEffect( - type=BuffType.INVINCIBLE2, - duration=4, - source=self - ) - dizzy = BuffEffect( - type=BuffType.DIZZY, - duration=4, - source=self + type=BuffType.INVINCIBLE2, duration=4, source=self ) + dizzy = BuffEffect(type=BuffType.DIZZY, duration=4, source=self) # 转阶段 self.status_system.apply(switch_stage) self.status_system.apply(dizzy) @@ -1213,7 +1324,10 @@ def on_extra_update(self, delta_time): if self.stage == 1: # 蓄力5秒后造成攻击力200%法术伤害 - if self.charging_counter >= 5 or not self.locked_target.can_be_target(): + if ( + self.charging_counter >= 5 + or not self.locked_target.can_be_target() + ): self.stage = 2 self.move_speed = self.original_move_speed self.skill_counter = 0 @@ -1225,7 +1339,9 @@ def on_extra_update(self, delta_time): debug_print(f"{self.name}{self.id} 退出蓄力") if self.locked_target.can_be_target(): - damage = self.calculate_damage(self.locked_target, self.get_attack_power() * 2) + damage = self.calculate_damage( + self.locked_target, self.get_attack_power() * 2 + ) self.on_attack(self.locked_target, damage) if self.apply_damage_to_target(self.locked_target, damage): self.locked_target.on_hit(self, damage) @@ -1281,10 +1397,12 @@ def on_extra_update(self, delta_time): self.stage = 1 self.move_speed = 0 self.charging_counter = 0 - #self.target_pos = self.target.position - self.target_pos = FastVector(self.target.position.x, self.target.position.y) + # self.target_pos = self.target.position + self.target_pos = FastVector( + self.target.position.x, self.target.position.y + ) debug_print(f"{self.name}{self.id} 开始蓄力") - #debug_print(f"{self.name}{self.id} 锁定的坐标是{self.target_pos.x},{self.target_pos.y}") + # debug_print(f"{self.name}{self.id} 锁定的坐标是{self.target_pos.x},{self.target_pos.y}") if self.stage == 1: # 蓄力7秒后造成攻击力250%法术伤害 if self.charging_counter >= 7: @@ -1292,15 +1410,21 @@ def on_extra_update(self, delta_time): self.move_speed = self.original_move_speed self.skill_counter = 0 self.charging_counter = 0 - #debug_print(f"{self.name}{self.id} 轰炸的中心是{self.target_pos.x},{self.target_pos.y}") + # debug_print(f"{self.name}{self.id} 轰炸的中心是{self.target_pos.x},{self.target_pos.y}") for m in self.battlefield.monsters: if m.faction != self.faction and m.can_be_target(): - #改为max(5|x|,|y|)<=2.5∪max(|x|,5|y|)<=2.5∪max(|x|,|y|)<=1.5 - x = int(math.floor(m.position.x - self.target_pos.x + 0.5)) - y = int(math.floor(m.position.y - self.target_pos.y + 0.5)) + # 改为max(5|x|,|y|)<=2.5∪max(|x|,5|y|)<=2.5∪max(|x|,|y|)<=1.5 + x = int( + math.floor(m.position.x - self.target_pos.x + 0.5) + ) + y = int( + math.floor(m.position.y - self.target_pos.y + 0.5) + ) if abs(x) + abs(y) <= 2: - dmg = self.calculate_damage(m, self.get_attack_power() * 2.5) + dmg = self.calculate_damage( + m, self.get_attack_power() * 2.5 + ) if self.apply_damage_to_target(m, dmg): m.on_hit(self, dmg) @@ -1398,19 +1522,31 @@ def get_hit_enemies(self): # 列坐标匹配(同一垂直方向) if abs(x - self_x) <= 0.5: - if m.position.y > self.position.y and m.position.y - self.position.y < smallest_up: + if ( + m.position.y > self.position.y + and m.position.y - self.position.y < smallest_up + ): smallest_up = m.position.y - self.position.y smallest_up_target = m - if m.position.y < self.position.y and self.position.y - m.position.y < smallest_down: + if ( + m.position.y < self.position.y + and self.position.y - m.position.y < smallest_down + ): smallest_down = self.position.y - m.position.y smallest_down_target = m # 行坐标匹配(同一水平方向) if abs(y - self_y) <= 0.5: - if m.position.x > self.position.x and m.position.x - self.position.x < smallest_right: + if ( + m.position.x > self.position.x + and m.position.x - self.position.x < smallest_right + ): smallest_right = m.position.x - self.position.x smallest_right_target = m - if m.position.x < self.position.x and self.position.x - m.position.x < smallest_left: + if ( + m.position.x < self.position.x + and self.position.x - m.position.x < smallest_left + ): smallest_left = self.position.x - m.position.x smallest_left_target = m @@ -1451,7 +1587,8 @@ def on_spawn(self): class AttackNode: """攻击节点数据类""" - __slots__ = ['target', 'damage_multiplier'] # 优化内存使用 + + __slots__ = ["target", "damage_multiplier"] # 优化内存使用 def __init__(self, target, multiplier): self.target = target @@ -1470,7 +1607,9 @@ def chain_attack(self, initial_target: Monster) -> list[AttackNode]: current_multiplier = 1.0 # 添加初始攻击 - attack_chain.append(self.AttackNode(current_target, current_multiplier)) + attack_chain.append( + self.AttackNode(current_target, current_multiplier) + ) visited.add(current_target.id) # 执行最多4次跳跃 @@ -1478,8 +1617,12 @@ def chain_attack(self, initial_target: Monster) -> list[AttackNode]: # 寻找下一个候选目标 candidates = self._find_candidates( current_target.position, - [m for m in self.battlefield.alive_monsters if m.can_be_target() and m.faction != self.faction], - visited + [ + m + for m in self.battlefield.alive_monsters + if m.can_be_target() and m.faction != self.faction + ], + visited, ) if not candidates: @@ -1498,10 +1641,9 @@ def chain_attack(self, initial_target: Monster) -> list[AttackNode]: return attack_chain - def _find_candidates(self, - origin: FastVector, - enemies: List['Monster'], - visited: set) -> List[tuple]: + def _find_candidates( + self, origin: FastVector, enemies: List["Monster"], visited: set + ) -> List[tuple]: """ 查找有效候选目标 :param origin: 当前攻击源点坐标 (x, y) @@ -1534,25 +1676,25 @@ def attack(self, target, gameTime): node.target.on_hit(self, dmg) # 所有人都有一样的凋亡损伤 t = ElementType.NECRO_RIGHT - node.target.element_system.accumulate(t, self.get_attack_power() * 0.3) + node.target.element_system.accumulate( + t, self.get_attack_power() * 0.3 + ) # debug_print(f"{self.name}{self.id} 对 {node.target.name}{node.target.id} 造成{dmg}点魔法伤害") def on_death(self): debug_print(f"{self.name} 变成大君之赐") - m = self.battlefield.append_monster_name("大君之赐", self.faction, self.position + FastVector( - random.uniform(-1, 1) * 0.2, - random.uniform(-1, 1) * 0.2 - )) - switch_stage = BuffEffect( - type=BuffType.INVINCIBLE2, - duration=1, - source=self + m = self.battlefield.append_monster_name( + "大君之赐", + self.faction, + self.position + + FastVector( + random.uniform(-1, 1) * 0.2, random.uniform(-1, 1) * 0.2 + ), ) - dizzy = BuffEffect( - type=BuffType.DIZZY, - duration=1, - source=self + switch_stage = BuffEffect( + type=BuffType.INVINCIBLE2, duration=1, source=self ) + dizzy = BuffEffect(type=BuffType.DIZZY, duration=1, source=self) # 转阶段 m.status_system.apply(switch_stage) m.status_system.apply(dizzy) @@ -1580,7 +1722,11 @@ def on_spawn(self): def on_extra_update(self, delta_time): self.decay_timer += delta_time - if self.decay_timer > 3.5 and abs(round(self.decay_timer - 3.5) - (self.decay_timer - 3.5)) < 0.001: + if ( + self.decay_timer > 3.5 + and abs(round(self.decay_timer - 3.5) - (self.decay_timer - 3.5)) + < 0.001 + ): if self.attack_stack > 0: self.attack_stack -= 2 self.attack_multiplier -= 0.3 @@ -1619,14 +1765,23 @@ def on_spawn(self): self.attack_animation = AttackAnimation(0.05, 0.15, 0.8, self) def attack(self, target, gameTime): - targets: list[Monster] = TargetSelector.select_targets(self, self.battlefield, need_in_range=True, - max_targets=1) + targets: list[Monster] = TargetSelector.select_targets( + self, self.battlefield, need_in_range=True, max_targets=1 + ) if len(targets) == 0: return self.battlefield.projectiles_manager.spawn_projectile( - AOE炸弹锁定(0.2, self.get_attack_power(), self.attack_type, self, targets[0], name="火箭弹", - aoeType=AOEType.Grid8)) + AOE炸弹锁定( + 0.2, + self.get_attack_power(), + self.attack_type, + self, + targets[0], + name="火箭弹", + aoeType=AOEType.Grid8, + ) + ) debug_print(f"{self.name}{self.id} 开炮") @@ -1648,8 +1803,16 @@ def on_extra_update(self, delta_time): distance = direction.magnitude if distance <= self.attack_range: self.battlefield.projectiles_manager.spawn_projectile( - AOE炸弹锁定(0.2, self.get_attack_power() * 2, self.attack_type, self, self.target, - name="火箭弹", aoeType=AOEType.Grid8)) + AOE炸弹锁定( + 0.2, + self.get_attack_power() * 2, + self.attack_type, + self, + self.target, + name="火箭弹", + aoeType=AOEType.Grid8, + ) + ) self.stage = 1 debug_print(f"{self.name}{self.id} 射出火箭弹") @@ -1700,7 +1863,9 @@ def get_max_skill_bar(self): return super().get_max_skill_bar() def lock_target(self): - targets = TargetSelector.select_targets(self, self.battlefield, need_in_range=True, max_targets=1) + targets = TargetSelector.select_targets( + self, self.battlefield, need_in_range=True, max_targets=1 + ) if len(targets) > 0: return targets[0] return None @@ -1723,21 +1888,30 @@ def on_extra_update(self, delta_time): self.skill_counter = 0 else: # 法术伤害 - dmg = self.calculate_damage(self.locked_target, self.get_attack_power() * 0.4 * delta_time) + dmg = self.calculate_damage( + self.locked_target, + self.get_attack_power() * 0.4 * delta_time, + ) if self.apply_damage_to_target(self.locked_target, dmg): self.locked_target.on_hit(self, dmg) - # debug_print(f"{self.locked_target.name}{self.locked_target.id} 受到 {self.name}{self.id} 的{dmg}点法术伤害") + # debug_print(f"{self.locked_target.name}{self.locked_target.id} 受到 {self.name}{self.id} 的{dmg}点法术伤害") # 蓄力完成的凋亡损伤 - if self.locked_target.can_be_target() and self.charging_counter2 >= 8.0: + if ( + self.locked_target.can_be_target() + and self.charging_counter2 >= 8.0 + ): for m in self.get_aoe_targets(self.locked_target): dmg = self.get_attack_power() * 2.2 - m.element_system.accumulate(ElementType.NECRO_RIGHT, dmg) - debug_print(f"{m.name}{m.id} 受到 {self.name}{self.id} 的{dmg}点凋亡损伤") + m.element_system.accumulate( + ElementType.NECRO_RIGHT, dmg + ) + debug_print( + f"{m.name}{m.id} 受到 {self.name}{self.id} 的{dmg}点凋亡损伤" + ) self.stage = 0 self.move_speed = self.original_move_speed self.locked_target = None self.charging_counter2 = 0 - def get_aoe_targets(self, target): # 这个是获取|dx|<=1,|dy|<=1范围内的目标,是个矩形 @@ -1748,13 +1922,21 @@ def get_aoe_targets(self, target): # 十字:|dx|≤0.5∪|dy|≤0.5 # 矩形:max(|dx|,|dy|)≤1.5 # 十字与矩形取交集 - aoe_targets = [m for m in self.battlefield.monsters - if m.is_alive - and m.faction != self.faction - and np.maximum(abs(m.position.x - target.position.x), - abs(m.position.y - target.position.y)) <= 1.5 - and (abs(m.position.x - target.position.x) <= 0.5 or abs( - m.position.y - target.position.y) <= 0.5)] + aoe_targets = [ + m + for m in self.battlefield.monsters + if m.is_alive + and m.faction != self.faction + and np.maximum( + abs(m.position.x - target.position.x), + abs(m.position.y - target.position.y), + ) + <= 1.5 + and ( + abs(m.position.x - target.position.x) <= 0.5 + or abs(m.position.y - target.position.y) <= 0.5 + ) + ] return aoe_targets def attack(self, target, gameTime): @@ -1765,7 +1947,9 @@ def attack(self, target, gameTime): self.on_attack(target, damage) if self.apply_damage_to_target(target, damage): t = ElementType.NECRO_RIGHT - target.element_system.accumulate(t, self.get_attack_power() * 0.25) + target.element_system.accumulate( + t, self.get_attack_power() * 0.25 + ) target.on_hit(self, damage) @@ -1827,7 +2011,9 @@ def on_spawn(self): """标枪恐鱼穿刺者""" def attack(self, target, gameTime): - targets = TargetSelector.select_targets_lowest_health(self, self.battlefield, need_in_range=True, max_targets=1) + targets = TargetSelector.select_targets_lowest_health( + self, self.battlefield, need_in_range=True, max_targets=1 + ) if len(targets) == 0: return self.target = targets[0] @@ -1892,7 +2078,9 @@ def attack(self, target: Monster, gameTime): # 丢出酒桶以后 self.move_speed /= 3 self.stage = 1 - self.battlefield.add_new_zone(WineZone(target.position, self.battlefield, 12, self.faction)) + self.battlefield.add_new_zone( + WineZone(target.position, self.battlefield, 12, self.faction) + ) class 复仇者(Monster): @@ -1951,9 +2139,9 @@ def on_extra_update(self, delta_time): def on_death(self): enemies = enemies = [ - m for m in self.battlefield.monsters - if m.can_be_target() - and m.faction != self.faction + m + for m in self.battlefield.monsters + if m.can_be_target() and m.faction != self.faction ] if not enemies: return @@ -1991,9 +2179,7 @@ def on_extra_update(self, delta_time): self.move_speed = self.origial_move_speed * 3 self.speed_up_timer = 0 switch_stage = BuffEffect( - type=BuffType.INVINCIBLE, - duration=10, - source=self + type=BuffType.INVINCIBLE, duration=10, source=self ) self.status_system.apply(switch_stage) @@ -2049,21 +2235,32 @@ def increase_skill_cd(self, delta_time): if self.stage == 0: if self.target: for m in self.get_aoe_targets(self.target): - damage = self.calculate_damage(m, self.get_attack_power() * 2) + damage = self.calculate_damage( + m, self.get_attack_power() * 2 + ) if self.apply_damage_to_target(m, damage): m.on_hit(self, damage) self.skill1_timer = 0 elif self.stage == 2: # 第二形态还会对最远目标释放一次 - targets = TargetSelector.select_targets(self, self.battlefield, need_in_range=False, max_targets=1, - reverse=True) + targets = TargetSelector.select_targets( + self, + self.battlefield, + need_in_range=False, + max_targets=1, + reverse=True, + ) if len(targets) > 0: for m in self.get_aoe_targets(self.target): - damage = self.calculate_damage(m, self.get_attack_power() * 2) + damage = self.calculate_damage( + m, self.get_attack_power() * 2 + ) if self.apply_damage_to_target(m, damage): m.on_hit(self, damage) for m in self.get_aoe_targets(targets[0]): - damage = self.calculate_damage(m, self.get_attack_power() * 2) + damage = self.calculate_damage( + m, self.get_attack_power() * 2 + ) if self.apply_damage_to_target(m, damage): m.on_hit(self, damage) self.skill1_timer = 0 @@ -2077,7 +2274,9 @@ def increase_skill_cd(self, delta_time): if self.shield_timer > 15: if self.shield > 0: for m in self.get_aoe_targets_skill2(): - damage = self.calculate_damage(m, self.get_attack_power() * 8) + damage = self.calculate_damage( + m, self.get_attack_power() * 8 + ) if self.apply_damage_to_target(m, damage): m.on_hit(self, damage) self.shield = 0 @@ -2090,7 +2289,9 @@ def increase_skill_cd(self, delta_time): if self.shield_timer > 15: if self.shield > 0: for m in self.get_aoe_targets_skill2(): - damage = self.calculate_damage(m, self.get_attack_power() * 12) + damage = self.calculate_damage( + m, self.get_attack_power() * 12 + ) if self.apply_damage_to_target(m, damage): m.on_hit(self, damage) self.shield = 0 @@ -2100,15 +2301,24 @@ def increase_skill_cd(self, delta_time): # 十字aoe判定 def get_aoe_targets(self, target): - aoe_targets = [m for m in self.battlefield.monsters - if m.is_alive and m.faction != self.faction - and abs(m.position.x - target.position.x) <= 2 and abs(m.position.y - target.position.y) <= 2] + aoe_targets = [ + m + for m in self.battlefield.monsters + if m.is_alive + and m.faction != self.faction + and abs(m.position.x - target.position.x) <= 2 + and abs(m.position.y - target.position.y) <= 2 + ] return aoe_targets def get_aoe_targets_skill2(self): - aoe_targets = [m for m in self.battlefield.monsters - if m.is_alive and m.faction != self.faction - and (m.position - self.position).magnitude < 3] + aoe_targets = [ + m + for m in self.battlefield.monsters + if m.is_alive + and m.faction != self.faction + and (m.position - self.position).magnitude < 3 + ] return aoe_targets def take_damage(self, damage, attack_type) -> bool: @@ -2184,16 +2394,17 @@ class MonsterFactory: "雷德": 雷德, "自在": 自在, # "扎罗": 扎罗, - # 添加更多映射... - "炮击组长": 炮击组长 + "炮击组长": 炮击组长, } @classmethod def create_monster(cls, data, faction, position, battlefield): monster_type = data["名字"] if monster_type in cls._monster_classes: - m = cls._monster_classes[monster_type](data, faction, position, battlefield) + m = cls._monster_classes[monster_type]( + data, faction, position, battlefield + ) m.on_spawn() return m else: diff --git a/simulator/projectiles.py b/src/simulation/projectiles.py similarity index 52% rename from simulator/projectiles.py rename to src/simulation/projectiles.py index 15c0882..d31a249 100644 --- a/simulator/projectiles.py +++ b/src/simulation/projectiles.py @@ -10,11 +10,18 @@ if TYPE_CHECKING: # 仅用于IDE类型提示,不会真实导入 - from .monsters import Monster + from .monsters import Monster from .battle_field import Battlefield + class Projectile: - def __init__(self, max_lifetime, damage : float, damageType : DamageType, source : "Monster"): + def __init__( + self, + max_lifetime, + damage: float, + damageType: DamageType, + source: "Monster", + ): self.lifetime = 0 self.max_lifetime = max_lifetime self.is_alive = True @@ -27,9 +34,17 @@ def update(self, delta_time, battle_field): """需被子类重写""" raise NotImplementedError + # 组件类型实现 class HomingProjectile(Projectile): - def __init__(self, max_lifetime, damage : float, damageType : DamageType, source : "Monster", target_enemy: "Monster"): + def __init__( + self, + max_lifetime, + damage: float, + damageType: DamageType, + source: "Monster", + target_enemy: "Monster", + ): super().__init__(max_lifetime, damage, damageType, source) self.target = target_enemy # 敌人对象引用 @@ -37,18 +52,26 @@ def update(self, delta_time, battle_field): if not self.target.is_alive: self.is_alive = False return - + self.lifetime += delta_time if self.lifetime >= self.max_lifetime: self.on_timeout(battle_field) self.is_alive = False - + def on_timeout(self, battle_field): """需被子类重写""" raise NotImplementedError + class TimedProjectile(Projectile): - def __init__(self, max_lifetime, damage : float, damageType : DamageType, source : "Monster", target_position): + def __init__( + self, + max_lifetime, + damage: float, + damageType: DamageType, + source: "Monster", + target_position, + ): super().__init__(max_lifetime, damage, damageType, source) self.target_pos = FastVector(target_position.x, target_position.y) @@ -64,12 +87,12 @@ def on_impact(self, battle_field): class ProjectileManager: - def __init__(self, battle_field : 'Battlefield'): + def __init__(self, battle_field: "Battlefield"): self.projectiles = [] self.global_id_counter = 0 self.battle_field = battle_field - def spawn_projectile(self, projectile : Projectile): + def spawn_projectile(self, projectile: Projectile): """使用对象池创建射弹""" self.projectiles.append(projectile) projectile.id = self.global_id_counter @@ -88,61 +111,114 @@ class AOEType(Enum): Grid8 = "八格" Circle = "圆形" -def get_aoe_targets(source, target_pos, battle_field: 'Battlefield'): + +def get_aoe_targets(source, target_pos, battle_field: "Battlefield"): if source.aoe_Type == AOEType.Grid8: - aoe_targets = [m for m in battle_field.alive_monsters - if m.is_alive and m.faction != source.source.faction - and np.maximum(abs(m.position.x - target_pos.x), abs(m.position.y - target_pos.y)) <= 1] + aoe_targets = [ + m + for m in battle_field.alive_monsters + if m.is_alive + and m.faction != source.source.faction + and np.maximum( + abs(m.position.x - target_pos.x), + abs(m.position.y - target_pos.y), + ) + <= 1 + ] elif source.aoe_Type == AOEType.Grid4: - aoe_targets = [m for m in battle_field.alive_monsters - if m.is_alive and m.faction != source.source.faction - and abs(int(math.floor(m.position.x - target_pos.x + 0.5))) + abs(int(math.floor(m.position.y - target_pos.y + 0.5))) <= 1] + aoe_targets = [ + m + for m in battle_field.alive_monsters + if m.is_alive + and m.faction != source.source.faction + and abs(int(math.floor(m.position.x - target_pos.x + 0.5))) + + abs(int(math.floor(m.position.y - target_pos.y + 0.5))) + <= 1 + ] elif source.aoe_Type == AOEType.Circle: - aoe_targets = [m for m in battle_field.query_monster(target_pos, source.radius) - if m.is_alive and m.faction != source.source.faction] + aoe_targets = [ + m + for m in battle_field.query_monster(target_pos, source.radius) + if m.is_alive and m.faction != source.source.faction + ] return aoe_targets + class AOE炸弹(TimedProjectile): - def __init__(self, max_lifetime, damage : float, damageType : DamageType, source : "Monster", target_position, name : str, aoeType : AOEType, radius=1): - super().__init__(max_lifetime, damage, damageType, source, target_position) + def __init__( + self, + max_lifetime, + damage: float, + damageType: DamageType, + source: "Monster", + target_position, + name: str, + aoeType: AOEType, + radius=1, + ): + super().__init__( + max_lifetime, damage, damageType, source, target_position + ) self.name = name self.aoe_Type = aoeType self.radius = radius - def apply_damage_to_target(self, m : 'Monster', damage): - debug_print(f"{self.source.name}{self.source.id} 的{self.name}对 {m.name}{m.id} 造成{damage}点{self.damage_type}伤害") + def apply_damage_to_target(self, m: "Monster", damage): + debug_print( + f"{self.source.name}{self.source.id} 的{self.name}对 {m.name}{m.id} 造成{damage}点{self.damage_type}伤害" + ) if m.take_damage(damage, self.damage_type): return True - debug_print(f"{self.source.name}{self.source.id} 的{self.name}没有对 {m.name}{m.id}造成伤害") + debug_print( + f"{self.source.name}{self.source.id} 的{self.name}没有对 {m.name}{m.id}造成伤害" + ) return False - def on_impact(self, battle_field:'Battlefield'): + def on_impact(self, battle_field: "Battlefield"): aoe_targets = get_aoe_targets(self, self.target_pos, battle_field) for m in aoe_targets: - damage = calculate_normal_dmg(m.phy_def, m.magic_resist, self.damage, self.damage_type) + damage = calculate_normal_dmg( + m.phy_def, m.magic_resist, self.damage, self.damage_type + ) if self.apply_damage_to_target(m, damage): m.on_hit(self.source, damage) - + class AOE炸弹锁定(HomingProjectile): - def __init__(self, max_lifetime, damage : float, damageType : DamageType, source : "Monster", target : 'Monster', name : str, aoeType : AOEType, radius=1): + def __init__( + self, + max_lifetime, + damage: float, + damageType: DamageType, + source: "Monster", + target: "Monster", + name: str, + aoeType: AOEType, + radius=1, + ): super().__init__(max_lifetime, damage, damageType, source, target) self.name = name self.aoe_Type = aoeType self.radius = radius - def apply_damage_to_target(self, m : 'Monster', damage): - debug_print(f"{self.source.name}{self.source.id} 的{self.name}对 {m.name}{m.id} 造成{damage}点{self.damage_type}伤害") + def apply_damage_to_target(self, m: "Monster", damage): + debug_print( + f"{self.source.name}{self.source.id} 的{self.name}对 {m.name}{m.id} 造成{damage}点{self.damage_type}伤害" + ) if m.take_damage(damage, self.damage_type): return True - debug_print(f"{self.source.name}{self.source.id} 的{self.name}没有对 {m.name}{m.id}造成伤害") + debug_print( + f"{self.source.name}{self.source.id} 的{self.name}没有对 {m.name}{m.id}造成伤害" + ) return False - def on_timeout(self, battle_field:'Battlefield'): + def on_timeout(self, battle_field: "Battlefield"): # if not self.target.can_be_target(): # return aoe_targets = get_aoe_targets(self, self.target.position, battle_field) for m in aoe_targets: - damage = calculate_normal_dmg(m.phy_def, m.magic_resist, self.damage, self.damage_type) + damage = calculate_normal_dmg( + m.phy_def, m.magic_resist, self.damage, self.damage_type + ) if self.apply_damage_to_target(m, damage): m.on_hit(self.source, damage) diff --git a/src/simulation/scene.json b/src/simulation/scene.json new file mode 100644 index 0000000..2463240 --- /dev/null +++ b/src/simulation/scene.json @@ -0,0 +1,206 @@ +{ + "left": [ + { + "name": "酸液源石虫·α", + "count": 0 + }, + { + "name": "高能源石虫", + "count": 0 + }, + { + "name": "阿咬", + "count": 0 + }, + { + "name": "狂暴的猎狗pro", + "count": 0 + }, + { + "name": "提亚卡乌好战者", + "count": 0 + }, + { + "name": "污染躯壳", + "count": 0 + }, + { + "name": "大盾哥", + "count": 0 + }, + { + "name": "宿主流浪者", + "count": 0 + }, + { + "name": "光剑", + "count": 4 + }, + { + "name": "泥岩巨像", + "count": 0 + }, + { + "name": "呼啸骑士团学徒", + "count": 0 + }, + { + "name": "萨卡兹大剑手", + "count": 0 + }, + { + "name": "狂暴宿主组长", + "count": 0 + }, + { + "name": "海螺", + "count": 0 + }, + { + "name": "拳手囚犯", + "count": 0 + }, + { + "name": "高塔术师", + "count": 0 + }, + { + "name": "冰原术师", + "count": 0 + }, + { + "name": "矿脉守卫", + "count": 0 + }, + { + "name": "“庞贝”", + "count": 0 + }, + { + "name": "绵羊", + "count": 0 + }, + { + "name": "食腐狗", + "count": 0 + }, + { + "name": "温顺的武装驮兽", + "count": 3 + }, + { + "name": "鼠鼠", + "count": 4 + }, + { + "name": "“投石机”", + "count": 0 + }, + { + "name": "船长", + "count": 0 + } + ], + "right": [ + { + "name": "酸液源石虫·α", + "count": 0 + }, + { + "name": "高能源石虫", + "count": 0 + }, + { + "name": "阿咬", + "count": 0 + }, + { + "name": "狂暴的猎狗pro", + "count": 0 + }, + { + "name": "提亚卡乌好战者", + "count": 0 + }, + { + "name": "污染躯壳", + "count": 0 + }, + { + "name": "大盾哥", + "count": 0 + }, + { + "name": "宿主流浪者", + "count": 0 + }, + { + "name": "光剑", + "count": 0 + }, + { + "name": "泥岩巨像", + "count": 0 + }, + { + "name": "呼啸骑士团学徒", + "count": 0 + }, + { + "name": "萨卡兹大剑手", + "count": 0 + }, + { + "name": "狂暴宿主组长", + "count": 0 + }, + { + "name": "海螺", + "count": 0 + }, + { + "name": "拳手囚犯", + "count": 11 + }, + { + "name": "高塔术师", + "count": 0 + }, + { + "name": "冰原术师", + "count": 0 + }, + { + "name": "矿脉守卫", + "count": 0 + }, + { + "name": "“庞贝”", + "count": 3 + }, + { + "name": "绵羊", + "count": 9 + }, + { + "name": "食腐狗", + "count": 0 + }, + { + "name": "温顺的武装驮兽", + "count": 0 + }, + { + "name": "鼠鼠", + "count": 0 + }, + { + "name": "“投石机”", + "count": 0 + }, + { + "name": "船长", + "count": 0 + } + ] +} \ No newline at end of file diff --git a/simulator/simulate.py b/src/simulation/simulate.py similarity index 60% rename from simulator/simulate.py rename to src/simulation/simulate.py index 8b3a58b..fd38d9c 100644 --- a/simulator/simulate.py +++ b/src/simulation/simulate.py @@ -1,9 +1,4 @@ import json -import math -import random -import time -from enum import Enum -import numpy as np import pandas as pd from tqdm import tqdm @@ -19,66 +14,82 @@ def process_battle_data(csv_path): """ # 读取CSV文件(假设没有表头) df = pd.read_csv(csv_path, header=1) - + # 数据结构化处理 battle_records = [] - + for _, row in df.iterrows(): # 分解左右阵营数据 - left_data = row[0:56] # 1-56列 (0-based索引0-55) + left_data = row[0:56] # 1-56列 (0-based索引0-55) right_data = row[56:112] # 56-112列 (0-based索引56-111) - winner = row[112] # 69列 (0-based索引112) - + winner = row[112] # 69列 (0-based索引112) + # 构建阵营字典(ID从1开始) - left_army = {MONSTER_MAPPING[i]: int(count) for i, count in enumerate(left_data) if count > 0} - right_army = {MONSTER_MAPPING[i]: int(count) for i, count in enumerate(right_data) if count > 0} - + left_army = { + MONSTER_MAPPING[i]: int(count) + for i, count in enumerate(left_data) + if count > 0 + } + right_army = { + MONSTER_MAPPING[i]: int(count) + for i, count in enumerate(right_data) + if count > 0 + } + # 构建记录格式 battle_record = { "left": left_army, "right": right_army, - "result": "left" if winner == 'L' else "right" + "result": "left" if winner == "L" else "right", } - + battle_records.append(battle_record) - + return battle_records + def main(): """主函数""" # 加载怪物数据 - with open("arknight/monsters.json", encoding='utf-8') as f: + with open("arknight/monsters.json", encoding="utf-8") as f: monster_data = json.load(f)["monsters"] - + # with open("scene.json", encoding='utf-8') as f: # scene_config = json.load(f) # 使用示例,直接修改这里的csv文件就可以跑模拟 battle_data = process_battle_data("arknight/56fin2_66k.csv") - win = 0 matches = 0 for scene_config in tqdm(battle_data): if VISUALIZATION_MODE: - scene_config = {"left": {"宿主流浪者": 7, "污染躯壳": 14, "凋零萨卡兹": 5}, "right": {"大喷蛛": 4, "杰斯顿·威廉姆斯": 1, "衣架": 10}, "result": "right"} - + scene_config = { + "left": {"宿主流浪者": 7, "污染躯壳": 14, "凋零萨卡兹": 5}, + "right": {"大喷蛛": 4, "杰斯顿·威廉姆斯": 1, "衣架": 10}, + "result": "right", + } - #{ "left": { "护盾哥": 5, "污染躯壳": 11, "船长": 5 }, "right": { "炮击组长": 4, "沸血骑士团精锐": 4, "雪境精锐": 4}, "result": "left" } + # { "left": { "护盾哥": 5, "污染躯壳": 11, "船长": 5 }, "right": { "炮击组长": 4, "沸血骑士团精锐": 4, "雪境精锐": 4}, "result": "left" } # 用户配置 left_army = scene_config["left"] right_army = scene_config["right"] - + # 初始化战场 leftWins = 0 for i in range(3): battlefield = Battlefield(monster_data) - if not battlefield.setup_battle(left_army, right_army, monster_data): + if not battlefield.setup_battle( + left_army, right_army, monster_data + ): continue - + # 开始战斗 - if battlefield.run_battle(visualize=VISUALIZATION_MODE) == Faction.LEFT: + if ( + battlefield.run_battle(visualize=VISUALIZATION_MODE) + == Faction.LEFT + ): leftWins += 1 if leftWins >= 2: break @@ -86,14 +97,15 @@ def main(): break left_win = leftWins >= 2 - - if (left_win and scene_config["result"] == "left") or (not left_win and scene_config["result"] == "right"): + + if (left_win and scene_config["result"] == "left") or ( + not left_win and scene_config["result"] == "right" + ): win += 1 else: - with open("errors.json", encoding='utf-8', mode='+a') as f: + with open("errors.json", encoding="utf-8", mode="+a") as f: f.write(json.dumps(scene_config, ensure_ascii=False)) - f.write('\n') - + f.write("\n") matches += 1 print(f"当前胜率:{win} / {matches}") diff --git a/src/simulation/stats.py b/src/simulation/stats.py new file mode 100644 index 0000000..8ab77cf --- /dev/null +++ b/src/simulation/stats.py @@ -0,0 +1,191 @@ +import json +from collections import defaultdict +import numpy as np +import pandas as pd +from scipy.stats import fisher_exact +import matplotlib.pyplot as plt + +# 配置中文字体 +plt.rcParams["font.sans-serif"] = ["Microsoft YaHei"] +plt.rcParams["axes.unicode_minus"] = False + + +def calculate_significance(row): + """计算修正后的统计显著性""" + # 关键参数 + error_total = ( + row["under_estimate"] + row["over_estimate"] + ) # 错误样本总次数 + correct_total = row["total"] - error_total # 正确样本总次数 + + # 过滤无效数据 + if error_total == 0 or correct_total == 0: + return np.nan + + # 低估检验(单侧) + contingency_under = [ + [row["under_estimate"], row["over_estimate"]], + [0, correct_total], + ] + _, under_p = fisher_exact(contingency_under, alternative="greater") + + # 高估检验(单侧) + contingency_over = [ + [row["over_estimate"], row["under_estimate"]], + [0, correct_total], + ] + _, over_p = fisher_exact(contingency_over, alternative="greater") + + return under_p + over_p + + +def analyze_monster_balance(data_path): + # 初始化统计容器 + stats = defaultdict( + lambda: { + "total": 0, # 总出现次数 + "error": 0, # 错误预测中出现次数 + "under_estimate": 0, # 低估计数(实际应胜但预测失败) + "over_estimate": 0, # 高估计数(实际应败但预测成功) + } + ) + + # 数据加载与分析 + with open(data_path, encoding="utf-8") as f: + for line in f: + record = json.loads(line.strip()) + actual = record["result"] + pred = record["pred"] + + for side in ["left", "right"]: + for monster, count in record[side].items(): + # 基础统计 + stats[monster]["total"] += 1 + + # 仅统计预测错误的情况 + if pred != actual: + stats[monster]["error"] += 1 + + # 判断估计方向 + if side == actual: + stats[monster]["under_estimate"] += 1 + elif side != actual: + stats[monster]["over_estimate"] += 1 + + # 构建分析数据框 + df = pd.DataFrame.from_dict(stats, orient="index") + df = df[df["total"] > 0] # 过滤无效数据 + + # 计算统计指标 + df["error_rate"] = df["error"] / df["total"] + df["bias_score"] = (df["under_estimate"] - df["over_estimate"]) / df[ + "total" + ] + df["abs_bias"] = abs(df["bias_score"]) + + # # Fisher精确检验 + # def apply_fisher(row): + # contingency = [ + # [row['under_estimate'], row['over_estimate']], + # [row['total'] - row['under_estimate'], + # row['total'] - row['over_estimate']] + # ] + # _, p_value = fisher_exact(contingency) + # return p_value + + df["p_value"] = df.apply(calculate_significance, axis=1) + + # 筛选显著结果 (p<0.04 且总出现次数>1) + valid = df[(df["p_value"] < 0.04) & (df["total"] >= 1)] + + # 生成可视化 + plot_monster_bias(valid) + + return valid.sort_values("abs_bias", ascending=False) + + +def plot_monster_bias(df): + plt.figure(figsize=(12, 8)) + + # 设置颜色映射 + colors = np.where( + df["bias_score"] > 0, "#4C72B0", "#DD8452" # 蓝色表示低估 + ) # 橙色表示高估 + + # 绘制条形图 + bars = plt.barh(df.index, df["bias_score"], color=colors) + + # 添加统计标注 + for bar, (_, row) in zip(bars, df.iterrows()): + plt.text( + bar.get_width() + 0.02, + bar.get_y() + bar.get_height() / 2, + f"p={row['p_value']:.3f}\nN={row['total']}", + va="center", + ) + + # 图表装饰 + plt.axvline(0, color="gray", linestyle="--") + plt.title("怪物数值平衡性分析", fontsize=14) + plt.xlabel("偏差分数(正值表示低估,负值表示高估)") + plt.ylabel("怪物名称") + plt.xlim(-1.1, 1.1) + plt.grid(axis="x", alpha=0.3) + plt.tight_layout() + plt.savefig("monster_balance.png", dpi=300) + plt.close() + + +# 执行分析 +if __name__ == "__main__": + result_df = analyze_monster_balance("../errors.json") + + # 打印格式化结果 + print("=" * 60) + print( + f"{'怪物名称':<10}\t{'偏差方向':<8}\t{'偏差分数':<8}\t{'p值':<10}\t{'样本量'}" + ) + print("-" * 60) + for name, row in result_df.iterrows(): + direction = "低估" if row["bias_score"] > 0 else "高估" + print( + f"{name:<10}\t{direction:<8}\t{row['bias_score']:.2f}{'*' if row['p_value']<0.01 else '':<4}" + f"\t{row['p_value']:.3f}\t{'':<6}\t{row['total']}" + ) + + plot_monster_bias(result_df) + +with open("../errors.json", encoding="utf-8") as f: + data = f.read() + +counter = defaultdict(int) +power_counter = defaultdict(int) +err_counter = defaultdict(int) + +for line in data.strip().split("\n"): + record = json.loads(line) + result = record["result"] + pred = record["pred"] + for side in ["left", "right"]: + for monster, count in record[side].items(): + counter[monster] += 1 + if pred != result: + if side == result: + # 应该获胜却没有获胜,有低估的可能性 + power_counter[monster] += 1 + else: + # 不该获胜的获胜了,有高估的可能性 + power_counter[monster] -= 1 + err_counter[monster] += 1 + +sorted_monsters = sorted( + err_counter.items(), key=lambda x: (-x[1] / counter[x[0]], x[0]) +) + +for monster, count in sorted_monsters: + str = "低估" if power_counter[monster] > 0 else "高估" + total = counter[monster] + err_total = count + print( + f"{monster}: {err_total}/{total} = {err_total/total} 估计值:【{str}】{-power_counter[monster] / total * 100}" + ) diff --git a/simulator/utils.py b/src/simulation/utils.py similarity index 80% rename from simulator/utils.py rename to src/simulation/utils.py index d078175..d6d2ec6 100644 --- a/simulator/utils.py +++ b/src/simulation/utils.py @@ -1,11 +1,9 @@ - - from collections import defaultdict from dataclasses import dataclass, field from enum import Enum import logging import math -import csv # 添加csv模块导入 +import csv # 添加csv模块导入 import numpy as np @@ -21,14 +19,17 @@ VISUALIZATION_MODE = True + def debug_print(msg): if VISUALIZATION_MODE: print(msg) + class Faction(Enum): LEFT = 0 RIGHT = 1 + class DamageType(Enum): PHYSICAL = "物理" MAGIC = "法术" @@ -37,6 +38,7 @@ class DamageType(Enum): def __str__(self): return self.value # 直接返回值字符串 + class BuffType(Enum): CHILL = 0 FROZEN = 1 @@ -45,15 +47,17 @@ class BuffType(Enum): CORRUPT = 4 SPEEDUP = 5 DIZZY = 6 - POWER_STONE = 7 # 源石地板 - WINE = 8 # 酒桶的效果 - INVINCIBLE2 = 9 # 转阶段无敌,不会被设为目标 + POWER_STONE = 7 # 源石地板 + WINE = 8 # 酒桶的效果 + INVINCIBLE2 = 9 # 转阶段无敌,不会被设为目标 + class ElementType(Enum): NECRO_LEFT = "凋亡左" # 凋亡元素(原凋亡损伤) NECRO_RIGHT = "凋亡右" # 凋亡元素(原凋亡损伤) FIRE = "灼燃" + def lerp(a, b, x): return a + (b - a) * x @@ -67,9 +71,10 @@ class BuffEffect: data: dict = field(default_factory=dict) -VIRTUAL_TIME_STEP = 30 # 30帧相当于一秒 +VIRTUAL_TIME_STEP = 30 # 30帧相当于一秒 VIRTUAL_TIME_DELTA = 1.0 / VIRTUAL_TIME_STEP + def calculate_normal_dmg(defense, magic_resist, dmg, damageType: DamageType): """计算伤害值""" if damageType == DamageType.PHYSICAL: @@ -78,30 +83,30 @@ def calculate_normal_dmg(defense, magic_resist, dmg, damageType: DamageType): return np.maximum(dmg * 0.05, dmg * (1.0 - magic_resist / 100)) elif damageType == DamageType.TRUE: return dmg - + class SpatialHash: - def __init__(self, battle_field : 'Battlefield', cell_size=0.5): + def __init__(self, battle_field: "Battlefield", cell_size=0.5): self.cell_size = cell_size self.grid = defaultdict(set) # 使用集合避免重复 self.battle_field = battle_field # 记录战场,用来索引敌人信息 self.position_map = {} # 记录每个对象的老位置键 - def _pos_to_key(self, position : FastVector) -> tuple: + def _pos_to_key(self, position: FastVector) -> tuple: """将坐标转换为网格键""" return ( int(math.floor(position.x / self.cell_size)), - int(math.floor(position.y / self.cell_size)) + int(math.floor(position.y / self.cell_size)), ) - def insert(self, position : FastVector, id): + def insert(self, position: FastVector, id): """插入或更新对象位置""" new_key = self._pos_to_key(position) # 如果位置未变化,直接返回 if id in self.position_map and self.position_map[id] == new_key: return - + # 移除旧位置的记录 if id in self.position_map: old_key = self.position_map[id] @@ -117,18 +122,18 @@ def query_neighbors(self, position: FastVector, radius: float) -> set: """查询指定半径内的邻居""" center_x, center_y = (position.x, position.y) neighbors = set() - + # 生成需要检测的网格范围 min_i = int((center_x - radius) / self.cell_size) max_i = int((center_x + radius) / self.cell_size) min_j = int((center_y - radius) / self.cell_size) max_j = int((center_y + radius) / self.cell_size) - + # 遍历所有可能包含邻居的网格 for i in range(min_i, max_i + 1): for j in range(min_j, max_j + 1): neighbors.update(self.grid.get((i, j), set())) - + return neighbors def batch_update(self, updates: dict): @@ -136,27 +141,33 @@ def batch_update(self, updates: dict): for obj_id, pos in updates.items(): self.insert(obj_id, pos) -def load_monster_mapping_from_csv(file_path='monster.csv'): + +def load_monster_mapping_from_csv(file_path="src/resources/data/monster.csv"): """从CSV文件加载怪物ID和原始名称的映射""" mapping = {} try: - with open(file_path, mode='r', encoding='utf-8-sig') as csvfile: + with open(file_path, mode="r", encoding="utf-8-sig") as csvfile: reader = csv.DictReader(csvfile) for row in reader: try: - monster_id = int(row['id']) - 1 # 转为0-based索引 - original_name = row['原始名称'] + monster_id = int(row["id"]) - 1 # 转为0-based索引 + original_name = row["原始名称"] mapping[monster_id] = original_name except ValueError: - print(f"Skipping row due to invalid ID or missing '原始名称': {row}") + print( + f"Skipping row due to invalid ID or missing '原始名称': {row}" + ) except FileNotFoundError: print(f"Error: {file_path} not found. Using empty monster mapping.") except Exception as e: logger.exception(f"Error loading monster mapping from CSV: {e}") return mapping + # ID与怪物名称映射表 -MONSTER_MAPPING = load_monster_mapping_from_csv('monster_greenvine.csv') +MONSTER_MAPPING = load_monster_mapping_from_csv( + "src/resources/data/monster_greenvine.csv" +) # 创建反向映射字典(名字到ID) -REVERSE_MONSTER_MAPPING = {name: id for id, name in MONSTER_MAPPING.items()} \ No newline at end of file +REVERSE_MONSTER_MAPPING = {name: id for id, name in MONSTER_MAPPING.items()} diff --git a/simulator/vector2d.py b/src/simulation/vector2d.py similarity index 85% rename from simulator/vector2d.py rename to src/simulation/vector2d.py index a55ee2b..4841d93 100644 --- a/simulator/vector2d.py +++ b/src/simulation/vector2d.py @@ -1,5 +1,6 @@ import math from typing import Tuple + # import numpy as np # from numba.experimental import jitclass # from numba import float64 @@ -19,101 +20,103 @@ # # 基础运算 # def __add__(self, other: 'Vector2D') -> 'Vector2D': # return Vector2D(self.x + other.x, self.y + other.y) - + # def __sub__(self, other: 'Vector2D') -> 'Vector2D': # return Vector2D(self.x - other.x, self.y - other.y) - + # def __mul__(self, scalar: float) -> 'Vector2D': # return Vector2D(self.x * scalar, self.y * scalar) - + # # 高性能运算方法 # @property # def magnitude(self) -> float: # """模长计算优化(SIMD加速)""" # return math.hypot(self.x, self.y) # 使用hypot避免溢出 - + # def normalized(self) -> 'Vector2D': # """单位向量(零除保护)""" # mag = self.magnitude # return Vector2D(self.x/mag, self.y/mag) if mag != 0 else Vector2D(0, 0) - + # def dot(self, other: 'Vector2D') -> float: # """点积(寄存器优化)""" # return self.x*other.x + self.y*other.y - + # def rotate(self, radians: float) -> 'Vector2D': # """旋转优化(预计算sin/cos)""" # c = math.cos(radians) # s = math.sin(radians) # return Vector2D(self.x*c - self.y*s, self.x*s + self.y*c) + # 纯Python优化方案(兼容性更好) class FastVector: - __slots__ = ('x', 'y') # 减少内存占用约40% - + __slots__ = ("x", "y") # 减少内存占用约40% + def __init__(self, x: float, y: float): self.x = x self.y = y - - def __sub__(self, other: 'FastVector') -> 'FastVector': + + def __sub__(self, other: "FastVector") -> "FastVector": return self.__class__(self.x - other.x, self.y - other.y) - def __add__(self, other: 'FastVector') -> 'FastVector': + def __add__(self, other: "FastVector") -> "FastVector": return self.__class__(self.x + other.x, self.y + other.y) - - def __truediv__(self, other: float) -> 'FastVector': + + def __truediv__(self, other: float) -> "FastVector": return self.__class__(self.x / other, self.y / other) - - def __mul__(self, other: float) -> 'FastVector': + + def __mul__(self, other: float) -> "FastVector": return self.__class__(self.x * other, self.y * other) - - def __iadd__(self, other: 'FastVector') -> 'FastVector': + + def __iadd__(self, other: "FastVector") -> "FastVector": """就地加法优化(减少对象创建)""" self.x += other.x self.y += other.y return self - + @property def magnitude_sq(self) -> float: """平方模长(避免开平方)""" return self.x**2 + self.y**2 - + @property def magnitude(self) -> float: """模长""" return math.sqrt(self.x**2 + self.y**2) - - def distance_to(self, other: 'FastVector') -> float: + + def distance_to(self, other: "FastVector") -> float: """快速距离计算""" dx = self.x - other.x dy = self.y - other.y return math.hypot(dx, dy) - + def as_tuple(self) -> Tuple[float, float]: """缓存友好表示""" return (self.x, self.y) - def normalize(self) -> 'FastVector': + def normalize(self) -> "FastVector": d = self.magnitude if d != 0: self.x /= d self.y /= d return self + # # 性能对比测试 # if __name__ == '__main__': # from timeit import timeit - + # # 测试用例 # v1 = Vector2D(3.0, 4.0) # v2 = Vector2D(2.0, 1.0) - + # # Numba版本性能 # print("Numba Add:", timeit(lambda: v1 + v2, number=1_000_000)) # print("Numba Mag:", timeit(lambda: v1.magnitude, number=1_000_000)) - + # # 纯Python版本性能 # fv1 = FastVector(3.0, 4.0) # fv2 = FastVector(2.0, 1.0) # print("Python Add:", timeit(lambda: fv1 + fv2, number=1_000_000)) -# print("Python Mag:", timeit(lambda: fv1.magnitude_sq, number=1_000_000)) \ No newline at end of file +# print("Python Mag:", timeit(lambda: fv1.magnitude_sq, number=1_000_000)) diff --git a/simulator/zone.py b/src/simulation/zone.py similarity index 70% rename from simulator/zone.py rename to src/simulation/zone.py index 4bc5569..4522636 100644 --- a/simulator/zone.py +++ b/src/simulation/zone.py @@ -1,23 +1,21 @@ -from typing import List, Dict, Set from dataclasses import dataclass -import math -import time - -import numpy as np from .vector2d import FastVector from .utils import VIRTUAL_TIME_DELTA, BuffEffect, BuffType + class ZoneType: - POISON = 0 #毒圈 - WINE = 1 #酒桶区域 + POISON = 0 # 毒圈 + WINE = 1 # 酒桶区域 + @dataclass class ZoneEffect: type: str duration: float + class EffectZone: def __init__(self, zone_type: str, position, battle_field): self.zone_type = zone_type @@ -45,37 +43,46 @@ def apply_effect(self, target): raise NotImplementedError def remove_effect(self, target): - """移除区域效果""" + """移除区域效果""" raise NotImplementedError + class PoisonZone(EffectZone): def __init__(self, battle_field): super().__init__(ZoneType.POISON, FastVector(0, 0), battle_field) def apply_effect(self, target): # 添加或更新持续伤害效果 - target.status_system.apply(BuffEffect( - type=BuffType.POWER_STONE, - duration=VIRTUAL_TIME_DELTA * 2, - source=self - )) + target.status_system.apply( + BuffEffect( + type=BuffType.POWER_STONE, + duration=VIRTUAL_TIME_DELTA * 2, + source=self, + ) + ) def contains(self, target) -> bool: """判断点是否在区域内""" if self.battle_field.danger_zone_size() > 0: size = self.battle_field.danger_zone_size() - if (target.position.x < size + 1 or target.position.x > self.battle_field.map_size[0] - size - 1)\ - or (target.position.y < size or target.position.y > self.battle_field.map_size[1] - size): + if ( + target.position.x < size + 1 + or target.position.x > self.battle_field.map_size[0] - size - 1 + ) or ( + target.position.y < size + or target.position.y > self.battle_field.map_size[1] - size + ): return True return False - + + class WineZone(EffectZone): def __init__(self, position, battle_field, duration, faction): super().__init__(ZoneType.WINE, position, battle_field) self.duration = duration self.radius = 2 self.faction = faction - + def should_clear(self, delta_time) -> bool: """场地效果清除逻辑""" return self.duration <= 0 @@ -86,12 +93,16 @@ def update(self, delta_time): def apply_effect(self, target): # 添加或更新持续伤害效果 - target.status_system.apply(BuffEffect( - type=BuffType.WINE, - duration=VIRTUAL_TIME_DELTA * 2, - source=self - )) + target.status_system.apply( + BuffEffect( + type=BuffType.WINE, + duration=VIRTUAL_TIME_DELTA * 2, + source=self, + ) + ) def contains(self, target) -> bool: """判断点是否在区域内""" - return (target.position - self.position).magnitude <= self.radius and target.faction == self.faction \ No newline at end of file + return ( + target.position - self.position + ).magnitude <= self.radius and target.faction == self.faction diff --git a/tools/HumanDataCheck.py b/src/tools/HumanDataCheck.py similarity index 58% rename from tools/HumanDataCheck.py rename to src/tools/HumanDataCheck.py index 692187e..c593057 100644 --- a/tools/HumanDataCheck.py +++ b/src/tools/HumanDataCheck.py @@ -3,6 +3,9 @@ import csv import os +from src.core.paths import PROJECT_ROOT, image_path, data_path + + class ArknightsApp: def __init__(self, root): self.root = root @@ -14,9 +17,15 @@ def __init__(self, root): self.root.configure(bg=self.BG_COLOR) # 绑定快捷键 - self.root.bind("", lambda event: self.show_prev_row()) # 小键盘左键 - self.root.bind("", lambda event: self.show_next_row()) # 小键盘右键 - self.root.bind("", lambda event: self.delete_current_row()) # 删除键 + self.root.bind( + "", lambda event: self.show_prev_row() + ) # 小键盘左键 + self.root.bind( + "", lambda event: self.show_next_row() + ) # 小键盘右键 + self.root.bind( + "", lambda event: self.delete_current_row() + ) # 删除键 # 创建顶部和底部框架 self.top_frame = tk.Frame(root, bg=self.BG_COLOR) @@ -25,28 +34,49 @@ def __init__(self, root): self.bottom_frame.pack(pady=10) # 创建按钮 - btn_style = {"bg": "#555555", "fg": self.FG_COLOR, "activebackground": "#777777", "activeforeground": self.FG_COLOR} - self.next_button = tk.Button(root, text="下一个", command=self.show_next_row, **btn_style) + btn_style = { + "bg": "#555555", + "fg": self.FG_COLOR, + "activebackground": "#777777", + "activeforeground": self.FG_COLOR, + } + self.next_button = tk.Button( + root, text="下一个", command=self.show_next_row, **btn_style + ) self.next_button.pack(side=tk.RIGHT, padx=10, pady=10) - self.prev_button = tk.Button(root, text="上一个", command=self.show_prev_row, **btn_style) + self.prev_button = tk.Button( + root, text="上一个", command=self.show_prev_row, **btn_style + ) self.prev_button.pack(side=tk.RIGHT, padx=10, pady=10) - self.delete_button = tk.Button(root, text="删除数据", command=self.delete_current_row, **btn_style) + self.delete_button = tk.Button( + root, text="删除数据", command=self.delete_current_row, **btn_style + ) self.delete_button.pack(side=tk.RIGHT, padx=10, pady=10) # 添加行号显示和跳转功能 - self.row_label = tk.Label(root, text="当前行号: 0", bg=self.BG_COLOR, fg=self.FG_COLOR) + self.row_label = tk.Label( + root, text="当前行号: 0", bg=self.BG_COLOR, fg=self.FG_COLOR + ) self.row_label.pack(side=tk.LEFT, padx=10) - self.row_entry = tk.Entry(root, width=5, bg="#555555", fg=self.FG_COLOR, insertbackground=self.FG_COLOR) + self.row_entry = tk.Entry( + root, + width=5, + bg="#555555", + fg=self.FG_COLOR, + insertbackground=self.FG_COLOR, + ) self.row_entry.pack(side=tk.LEFT, padx=5) - self.jump_button = tk.Button(root, text="跳转", command=self.jump_to_row, **btn_style) + self.jump_button = tk.Button( + root, text="跳转", command=self.jump_to_row, **btn_style + ) self.jump_button.pack(side=tk.LEFT, padx=5) # 初始化数据 - self.data = self.read_csv("arknights.csv") + self.data = self.read_csv(PROJECT_ROOT / "data" / "raw" / "arknights.csv") self.current_row_index = 0 # 加载图片 @@ -61,7 +91,7 @@ def read_csv(self, file_path): with open(file_path, "r", encoding="utf-8") as csvfile: reader = csv.reader(csvfile) header = next(reader) - self.MONSTER_COUNT = sum(1 for col in header if col.endswith('L')) + self.MONSTER_COUNT = sum(1 for col in header if col.endswith("L")) for row in reader: data.append(row) return data @@ -69,10 +99,12 @@ def read_csv(self, file_path): def load_all_images(self): """加载所有图片""" images = {} - base_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - monster_csv_path = os.path.join(base_dir, "monster_greenvine.csv") - images_dir = os.path.join(base_dir, "images") - + base_dir = os.path.dirname( + os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + ) + monster_csv_path = data_path("monster_greenvine.csv") + images_dir = data_path("images") + id_to_name = {} try: with open(monster_csv_path, "r", encoding="utf-8-sig") as f: @@ -94,7 +126,9 @@ def load_all_images(self): else: # 尝试用“名称”列备用 try: - with open(monster_csv_path, "r", encoding="utf-8-sig") as f: + with open( + monster_csv_path, "r", encoding="utf-8-sig" + ) as f: reader = csv.reader(f) header = next(reader) id_idx = header.index("id") @@ -102,9 +136,13 @@ def load_all_images(self): for row in reader: if int(row[id_idx]) == i: alt_name = row[alt_name_idx] - alt_path = os.path.join(images_dir, f"{alt_name}.png") + alt_path = os.path.join( + images_dir, f"{alt_name}.png" + ) if os.path.exists(alt_path): - image = Image.open(alt_path).resize((80, 80)) + image = Image.open(alt_path).resize( + (80, 80) + ) images[str(i)] = ImageTk.PhotoImage(image) break except Exception: @@ -132,16 +170,36 @@ def show_row(self, row_index): value = float(value) if value > 0: # 仅显示非0值 if self.images.get(str(i)): - tk.Label(self.top_frame, image=self.images[str(i)], bg=self.BG_COLOR).grid(row=0, column=i - 1, padx=2) + tk.Label( + self.top_frame, + image=self.images[str(i)], + bg=self.BG_COLOR, + ).grid(row=0, column=i - 1, padx=2) else: - tk.Label(self.top_frame, text=f"ID:{i}", font=("Arial", 12), bg=self.BG_COLOR, fg=self.FG_COLOR).grid(row=0, column=i - 1, padx=2) - tk.Label(self.top_frame, text=str(int(value)), font=("Arial", 16, "bold"), bg=self.BG_COLOR, fg=self.FG_COLOR).grid(row=1, column=i - 1) + tk.Label( + self.top_frame, + text=f"ID:{i}", + font=("Arial", 12), + bg=self.BG_COLOR, + fg=self.FG_COLOR, + ).grid(row=0, column=i - 1, padx=2) + tk.Label( + self.top_frame, + text=str(int(value)), + font=("Arial", 16, "bold"), + bg=self.BG_COLOR, + fg=self.FG_COLOR, + ).grid(row=1, column=i - 1) except ValueError: - print(f"Skipping invalid value: {row[i - 1]} at column {i - 1}") + print( + f"Skipping invalid value: {row[i - 1]} at column {i - 1}" + ) # 插入空白间隔 gap_column = self.MONSTER_COUNT - 1 # 间隔列索引 - tk.Label(self.top_frame, text="", bg=self.BG_COLOR).grid(row=0, column=gap_column, padx=50) # 添加水平间距 + tk.Label(self.top_frame, text="", bg=self.BG_COLOR).grid( + row=0, column=gap_column, padx=50 + ) # 添加水平间距 # 显示右方怪物 for i in range(self.MONSTER_COUNT + 1, self.MONSTER_COUNT * 2 + 1): @@ -151,21 +209,39 @@ def show_row(self, row_index): if value > 0: # 仅显示非0值 img_idx = str(i - self.MONSTER_COUNT) if self.images.get(img_idx): - tk.Label(self.top_frame, image=self.images[img_idx], bg=self.BG_COLOR).grid(row=0, column=i - 1, padx=2) + tk.Label( + self.top_frame, + image=self.images[img_idx], + bg=self.BG_COLOR, + ).grid(row=0, column=i - 1, padx=2) else: - tk.Label(self.top_frame, text=f"ID:{img_idx}", font=("Arial", 12), bg=self.BG_COLOR, fg=self.FG_COLOR).grid(row=0, column=i - 1, padx=2) - tk.Label(self.top_frame, text=str(int(value)), font=("Arial", 16, "bold"), bg=self.BG_COLOR, fg=self.FG_COLOR).grid(row=1, column=i - 1) + tk.Label( + self.top_frame, + text=f"ID:{img_idx}", + font=("Arial", 12), + bg=self.BG_COLOR, + fg=self.FG_COLOR, + ).grid(row=0, column=i - 1, padx=2) + tk.Label( + self.top_frame, + text=str(int(value)), + font=("Arial", 16, "bold"), + bg=self.BG_COLOR, + fg=self.FG_COLOR, + ).grid(row=1, column=i - 1) except ValueError: - print(f"Skipping invalid value: {row[i - 1]} at column {i - 1}") + print( + f"Skipping invalid value: {row[i - 1]} at column {i - 1}" + ) # 清空底部框架内容 for widget in self.bottom_frame.winfo_children(): - widget.destroy() + widget.destroy() # 获取图片路径 base_dir = os.path.dirname(os.path.abspath(__file__)) img_name = row[-1] - + orig_path = os.path.join(base_dir, "images", img_name + ".jpg") res_path = os.path.join(base_dir, "images", img_name + "_result.jpg") @@ -175,9 +251,11 @@ def show_row(self, row_index): orig_path = os.path.join(base_dir, img_name + ".jpg") if not os.path.exists(orig_path): orig_path = None - + if not os.path.exists(res_path): - res_path = os.path.join(base_dir, "images", img_name + "_result.png") + res_path = os.path.join( + base_dir, "images", img_name + "_result.png" + ) if not os.path.exists(res_path): res_path = None @@ -192,37 +270,78 @@ def show_row(self, row_index): top = h * 0.8 right = w * 0.8 bottom = h - cropped_image = orig_image.crop((int(left), int(top), int(right), int(bottom))) - + cropped_image = orig_image.crop( + (int(left), int(top), int(right), int(bottom)) + ) + # 如果需要限制大小,可解除下面注释 # max_size = (800, 200) # cropped_image.thumbnail(max_size, Image.Resampling.LANCZOS) - + cropped_image_tk = ImageTk.PhotoImage(cropped_image) - tk.Label(self.bottom_frame, image=cropped_image_tk, bg=self.BG_COLOR).pack(side=tk.TOP, pady=5) + tk.Label( + self.bottom_frame, image=cropped_image_tk, bg=self.BG_COLOR + ).pack(side=tk.TOP, pady=5) self.bottom_frame.images.append(cropped_image_tk) else: - tk.Label(self.bottom_frame, text=f"找不到原图: {img_name}", bg=self.BG_COLOR, fg=self.FG_COLOR).pack(side=tk.TOP, pady=5) + tk.Label( + self.bottom_frame, + text=f"找不到原图: {img_name}", + bg=self.BG_COLOR, + fg=self.FG_COLOR, + ).pack(side=tk.TOP, pady=5) # 2. 显示 L/R 文字和色条(中间) result_text = row[self.MONSTER_COUNT * 2].strip() - tk.Label(self.bottom_frame, text=result_text, font=("Arial", 20, "bold"), bg=self.BG_COLOR, fg=self.FG_COLOR).pack(side=tk.TOP, pady=2) - + tk.Label( + self.bottom_frame, + text=result_text, + font=("Arial", 20, "bold"), + bg=self.BG_COLOR, + fg=self.FG_COLOR, + ).pack(side=tk.TOP, pady=2) + # 添加色彩长条 Canvas bar_width = 200 bar_height = 15 - color_bar = tk.Canvas(self.bottom_frame, width=bar_width, height=bar_height, highlightthickness=0, bg=self.BG_COLOR) + color_bar = tk.Canvas( + self.bottom_frame, + width=bar_width, + height=bar_height, + highlightthickness=0, + bg=self.BG_COLOR, + ) color_bar.pack(side=tk.TOP, pady=5) - - if result_text.upper() == 'L': - color_bar.create_rectangle(0, 0, bar_width // 2, bar_height, fill="yellow", outline="") - color_bar.create_rectangle(bar_width // 2, 0, bar_width, bar_height, fill="gray", outline="") - elif result_text.upper() == 'R': - color_bar.create_rectangle(0, 0, bar_width // 2, bar_height, fill="gray", outline="") - color_bar.create_rectangle(bar_width // 2, 0, bar_width, bar_height, fill="yellow", outline="") + + if result_text.upper() == "L": + color_bar.create_rectangle( + 0, 0, bar_width // 2, bar_height, fill="yellow", outline="" + ) + color_bar.create_rectangle( + bar_width // 2, + 0, + bar_width, + bar_height, + fill="gray", + outline="", + ) + elif result_text.upper() == "R": + color_bar.create_rectangle( + 0, 0, bar_width // 2, bar_height, fill="gray", outline="" + ) + color_bar.create_rectangle( + bar_width // 2, + 0, + bar_width, + bar_height, + fill="yellow", + outline="", + ) else: # 默认颜色(异常数据时) - color_bar.create_rectangle(0, 0, bar_width, bar_height, fill="gray", outline="") + color_bar.create_rectangle( + 0, 0, bar_width, bar_height, fill="gray", outline="" + ) # 3. 显示结果图(下方) if res_path: @@ -230,10 +349,17 @@ def show_row(self, row_index): max_size = (800, 400) res_image.thumbnail(max_size, Image.Resampling.LANCZOS) res_image_tk = ImageTk.PhotoImage(res_image) - tk.Label(self.bottom_frame, image=res_image_tk, bg=self.BG_COLOR).pack(side=tk.TOP, pady=5) + tk.Label( + self.bottom_frame, image=res_image_tk, bg=self.BG_COLOR + ).pack(side=tk.TOP, pady=5) self.bottom_frame.images.append(res_image_tk) else: - tk.Label(self.bottom_frame, text=f"找不到结果图", bg=self.BG_COLOR, fg=self.FG_COLOR).pack(side=tk.TOP, pady=5) + tk.Label( + self.bottom_frame, + text=f"找不到结果图", + bg=self.BG_COLOR, + fg=self.FG_COLOR, + ).pack(side=tk.TOP, pady=5) # 更新行号显示 self.row_label.config(text=f"当前行号: {row_index + 1}") @@ -272,7 +398,7 @@ def delete_current_row(self): # 获取当前行最后一列的图片路径 image_name = self.data[self.current_row_index][-1] base_dir = os.path.dirname(os.path.abspath(__file__)) - + # 删除相关的图片文件(如果存在) possible_exts = [".jpg", "_result.jpg", ".png", ""] for ext in possible_exts: @@ -283,12 +409,14 @@ def delete_current_row(self): del self.data[self.current_row_index] # 从内存中删除当前行 # 将修改后的数据写回 CSV 文件 - with open("arknights.csv", "w", newline="", encoding="utf-8") as csvfile: + with open( + "arknights.csv", "w", newline="", encoding="utf-8" + ) as csvfile: writer = csv.writer(csvfile) writer.writerow( - [f"{i}L" for i in range(1, self.MONSTER_COUNT + 1)] + - [f"{i}R" for i in range(1, self.MONSTER_COUNT + 1)] + - ["Result", "ImgPath"] + [f"{i}L" for i in range(1, self.MONSTER_COUNT + 1)] + + [f"{i}R" for i in range(1, self.MONSTER_COUNT + 1)] + + ["Result", "ImgPath"] ) writer.writerows(self.data) # 更新显示 @@ -301,4 +429,4 @@ def delete_current_row(self): if __name__ == "__main__": root = tk.Tk() app = ArknightsApp(root) - root.mainloop() \ No newline at end of file + root.mainloop() diff --git a/tools/battlefield_composite/.gitignore b/src/tools/battlefield_composite/.gitignore similarity index 100% rename from tools/battlefield_composite/.gitignore rename to src/tools/battlefield_composite/.gitignore diff --git a/tools/battlefield_composite/battlefield_composite.py b/src/tools/battlefield_composite/battlefield_composite.py similarity index 87% rename from tools/battlefield_composite/battlefield_composite.py rename to src/tools/battlefield_composite/battlefield_composite.py index 58dd16e..becd82c 100644 --- a/tools/battlefield_composite/battlefield_composite.py +++ b/src/tools/battlefield_composite/battlefield_composite.py @@ -67,7 +67,9 @@ def get_random_png_frame(png_folder): # 随机选择一个png文件 random_png = random.choice(png_list) print(f"随机选择的PNG文件: {random_png}") - frame = cv2.imread(str(random_png), cv2.IMREAD_UNCHANGED) # Read with alpha channel + frame = cv2.imread( + str(random_png), cv2.IMREAD_UNCHANGED + ) # Read with alpha channel if frame is None: raise Exception(f"无法读取PNG文件: {random_png}") return frame @@ -123,7 +125,10 @@ def compose_frame(frame, background, x, y): # 进行alpha混合 for c in range(0, 3): - roi[:, :, c] = roi[:, :, c] * (1 - alpha_channel) + overlay_colors[:, :, c] * alpha_channel + roi[:, :, c] = ( + roi[:, :, c] * (1 - alpha_channel) + + overlay_colors[:, :, c] * alpha_channel + ) # 将结果放回原图 background[y_start:y_end, x_start:x_end] = roi @@ -133,7 +138,9 @@ def compose_frame(frame, background, x, y): def composite_random_frame(): # 读取战场背景图 - battlefield = cv2.imread("./tools/battlefield_composite/monster_images/IM-1.png") + battlefield = cv2.imread( + "./tools/battlefield_composite/monster_images/IM-1.png" + ) battlefield = cv2.resize(battlefield, (1920, 1080)) if battlefield is None: raise Exception("无法读取战场背景图") @@ -156,7 +163,9 @@ def composite_random_frame(): for i in range(10): x, y = frame_list[i] - frame = get_random_png_frame("./tools/battlefield_composite/monster_images/Arc_Frontliner_Leader-Move") + frame = get_random_png_frame( + "./tools/battlefield_composite/monster_images/Arc_Frontliner_Leader-Move" + ) # 缩放 factor = (y / bg_height) * 0.4 + 0.6 @@ -173,9 +182,16 @@ def composite_random_frame(): ellipse_x = int(x + center[0]) ellipse_y = int(y + center[1] + new_frame_height * 0.30) black = (0, 0, 0) - cv2.ellipse(battlefield, (ellipse_x, ellipse_y), - (int(new_frame_width * 0.100), int(new_frame_height * 0.025)), - 0, 0, 360, black, -1) + cv2.ellipse( + battlefield, + (ellipse_x, ellipse_y), + (int(new_frame_width * 0.100), int(new_frame_height * 0.025)), + 0, + 0, + 360, + black, + -1, + ) # 画框 cv2.rectangle(battlefield, (x, y), (x + x1, y + y1), (0, 255, 0), 2) @@ -196,9 +212,10 @@ def composite_random_frame(): # 将渐变掩码应用到图像上 battlefield = compose_frame(gradient_mask, battlefield, 0, 0) - # 保存结果 - cv2.imwrite("./tools/battlefield_composite/battlefield_composite.png", battlefield) + cv2.imwrite( + "./tools/battlefield_composite/battlefield_composite.png", battlefield + ) def crop_to_bounding_box(image): @@ -214,7 +231,9 @@ def crop_to_bounding_box(image): # 检查图像是否包含alpha通道 if image.shape[2] < 4: # 如果没有alpha通道,则无法进行基于透明度的裁切 - print("警告: 图像不包含alpha通道,无法进行基于透明度的裁切。返回原图。") + print( + "警告: 图像不包含alpha通道,无法进行基于透明度的裁切。返回原图。" + ) return image # 获取alpha通道 @@ -234,7 +253,7 @@ def crop_to_bounding_box(image): x, y, w, h = cv2.boundingRect(coords) # 裁切图像 - cropped_image = image[y:y+h, x:x+w] + cropped_image = image[y : y + h, x : x + w] center_x = image.shape[1] / 2 - x center_y = image.shape[0] / 2 - y center = (center_x, center_y) diff --git a/src/tools/battlefield_composite/extract_webm_frames.py b/src/tools/battlefield_composite/extract_webm_frames.py new file mode 100644 index 0000000..3b667d7 --- /dev/null +++ b/src/tools/battlefield_composite/extract_webm_frames.py @@ -0,0 +1,93 @@ +import logging +from pathlib import Path +import subprocess + +monster_name = { + "弧光锋卫长": "Arc_Frontliner_Leader", + "炮击组长": "Mortar_Gunner_Leader", + "复仇者": "Hateful_Avenger", + "重装防御者": "Heavy_Defender", + "“庞贝”": "Pompeii", + "冰原术师": "Icefield_Caster", + "沸血骑士团精锐": "Bloodboil_Knightclub_Elite", + "高塔术师": "Spire_Caster", + "固海凿石者": "Ocean_Stonecutter", + # "呼啸骑士团学徒" : "Roar_Knightclub_Trainee", + "湖畔志愿者": "Lakeside_Volunteer", + "杰斯顿·威廉姆斯": "Jesselton_Williams", + "酸液源石虫·α": "Acid_Originium_Slug_α", + "神射手囚犯": "Elite_Sniper_Prisoner", + "拳手囚犯": "Pugilist_Prisoner", + "染污躯壳": "Tainted_Carcass", + "泥岩巨像": "Mudrock_Colossus", + "狂暴的猎狗pro": "Rabid_Hound_Pro", + "宿主拾荒者": "Possessed_Veteran_Junkman", + "狂暴宿主组长": "Enraged_Possessed_Leader", + "萨卡兹大剑手": "Sarkaz_Greatswordsman", + "矿脉守卫": "Vein_Guardian", + "山海众窥魅人": "Shanhaizhong_Seer", + "提亚卡乌好战者": "Tiacauh_Fanatic", + "码头水手": "Dockworker", + "变异巨岩蛛": "Mutant_Giant_Rock_Spider", + "萨卡兹子裔链术师": "Sarkaz_Heirbearer_Chain_Caster", + "温顺的武装驮兽": "Armored_Burdenbeast", + "木裂战士": "Shattered_Champion", + "深溟裂礁者": "Nethersea_Reefbreaker", + "山雪鬼": "Tschäggättä", + "高普尼克": "Gopnik", + "冰爆源石虫": "Infused_Glacial_Originium_Slug", + "反装甲步兵": "Anti-Armor_Infantry", + "“钳钳生风”": "Consortium_of_Pincers", + "富营养的穿刺者": "Nourished_Piercer", + "高级武装人员": "Senior_Armed_Militant", + "朗姆酒推荐者": "Rum_Connoisseur", + "烈酒级醒酒助手": "Whiskey-Grade_Waker-Upper", + "萨卡兹王庭军术师": "Sarkaz_Royal_Court_Caster", + # "灰尾香主" : "Greytail_Leader", + "阵地击人手": "Field_Bludgeoner", + "源石畸变体": "Originiutant", + "提亚卡乌破坏王": "Tiacauh_Annihilator", + "逐腐兽": "Rotchaser", + "高能源石虫": "Infused_Originium_Slug", + "风情街“星术师”": "Fashion_Street_Stellar_Caster", + "田鼷力士": "Fieldmus_Bruiser", + "残党萨克斯手": "Remnant_Saxophonist", + "散华骑士团学徒": "Nova_Knightclub_Trainee", + "“阿咬”": "Bitey", + # "“门”" : "", + "“投石机”": "Catapult", + # "“复仇者”" : '"Hateful Avenger"', + # "扎罗,“狼之主”" : "Zaaro", + # "“自在”" : "Free", + # "灼热源石虫" : "Blazing_Originium_Slug", + # "萨卡兹子裔责罚者" : "Sarkaz_Heirbearer_Punisher", + # "" : "", +} + + +def extract_webm(webm_path: Path, output_folder: Path): + extract_webm_cmd = ( + f'ffmpeg -c:v libvpx -i "{webm_path}" "{output_folder}/frame%d.png"' + ) + subprocess.run(extract_webm_cmd, shell=True, check=True) + + +def main(): + webm_folder = "./tools/battlefield_composite/monster_images" + for webm_path in Path(webm_folder).glob("*.webm"): + name_split = webm_path.parts[-1].split("-") + print(name_split) + if (name := monster_name.get(name_split[0])) is not None: + output_directory = webm_path.parent / (name + "-" + name_split[3]) + print(output_directory) + if not output_directory.exists(): + output_directory.mkdir(exist_ok=True) + extract_webm(webm_path, output_directory) + else: + logging.warning(f"{output_directory} already exists") + else: + logging.error(f"Name: {name_split[0]} not found!") + + +if __name__ == "__main__": + main() diff --git a/tools/battlefield_composite/monster_images/IM-1.png b/src/tools/battlefield_composite/monster_images/IM-1.png similarity index 100% rename from tools/battlefield_composite/monster_images/IM-1.png rename to src/tools/battlefield_composite/monster_images/IM-1.png diff --git a/tools/battlefield_composite/monster_images/battlefield_empty.jpg b/src/tools/battlefield_composite/monster_images/battlefield_empty.jpg similarity index 100% rename from tools/battlefield_composite/monster_images/battlefield_empty.jpg rename to src/tools/battlefield_composite/monster_images/battlefield_empty.jpg diff --git "a/tools/battlefield_composite/monster_images/\342\200\234\345\244\215\344\273\207\350\200\205\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\342\200\234\345\244\215\344\273\207\350\200\205\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\342\200\234\345\244\215\344\273\207\350\200\205\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\342\200\234\345\244\215\344\273\207\350\200\205\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\342\200\234\345\272\236\350\264\235\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\342\200\234\345\272\236\350\264\235\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\342\200\234\345\272\236\350\264\235\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\342\200\234\345\272\236\350\264\235\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\342\200\234\346\212\225\347\237\263\346\234\272\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-A_Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\342\200\234\346\212\225\347\237\263\346\234\272\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-A_Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\342\200\234\346\212\225\347\237\263\346\234\272\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-A_Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\342\200\234\346\212\225\347\237\263\346\234\272\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-A_Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\342\200\234\350\207\252\345\234\250\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\342\200\234\350\207\252\345\234\250\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\342\200\234\350\207\252\345\234\250\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\342\200\234\350\207\252\345\234\250\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\342\200\234\351\222\263\351\222\263\347\224\237\351\243\216\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\342\200\234\351\222\263\351\222\263\347\224\237\351\243\216\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\342\200\234\351\222\263\351\222\263\347\224\237\351\243\216\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\342\200\234\351\222\263\351\222\263\347\224\237\351\243\216\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\342\200\234\351\227\250\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\342\200\234\351\227\250\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\342\200\234\351\227\250\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\342\200\234\351\227\250\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\342\200\234\351\230\277\345\222\254\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\342\200\234\351\230\277\345\222\254\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\342\200\234\351\230\277\345\222\254\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\342\200\234\351\230\277\345\222\254\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\345\206\260\345\216\237\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\345\206\260\345\216\237\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\345\206\260\345\216\237\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\345\206\260\345\216\237\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\345\206\260\347\210\206\346\272\220\347\237\263\350\231\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\345\206\260\347\210\206\346\272\220\347\237\263\350\231\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\345\206\260\347\210\206\346\272\220\347\237\263\350\231\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\345\206\260\347\210\206\346\272\220\347\237\263\350\231\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\345\217\215\350\243\205\347\224\262\346\255\245\345\205\265-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_A-x1.webm" "b/src/tools/battlefield_composite/monster_images/\345\217\215\350\243\205\347\224\262\346\255\245\345\205\265-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_A-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\345\217\215\350\243\205\347\224\262\346\255\245\345\205\265-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_A-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\345\217\215\350\243\205\347\224\262\346\255\245\345\205\265-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_A-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\345\217\230\345\274\202\345\267\250\345\262\251\350\233\233-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\345\217\230\345\274\202\345\267\250\345\262\251\350\233\233-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\345\217\230\345\274\202\345\267\250\345\262\251\350\233\233-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\345\217\230\345\274\202\345\267\250\345\262\251\350\233\233-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\345\221\274\345\225\270\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\345\221\274\345\225\270\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\345\221\274\345\225\270\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\345\221\274\345\225\270\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\345\233\272\346\265\267\345\207\277\347\237\263\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\345\233\272\346\265\267\345\207\277\347\237\263\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\345\233\272\346\265\267\345\207\277\347\237\263\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\345\233\272\346\265\267\345\207\277\347\237\263\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\345\244\215\344\273\207\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\345\244\215\344\273\207\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\345\244\215\344\273\207\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\345\244\215\344\273\207\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\345\256\277\344\270\273\346\213\276\350\215\222\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\345\256\277\344\270\273\346\213\276\350\215\222\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\345\256\277\344\270\273\346\213\276\350\215\222\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\345\256\277\344\270\273\346\213\276\350\215\222\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\345\257\214\350\220\245\345\205\273\347\232\204\347\251\277\345\210\272\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\345\257\214\350\220\245\345\205\273\347\232\204\347\251\277\345\210\272\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\345\257\214\350\220\245\345\205\273\347\232\204\347\251\277\345\210\272\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\345\257\214\350\220\245\345\205\273\347\232\204\347\251\277\345\210\272\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\345\261\261\346\265\267\344\274\227\347\252\245\351\255\205\344\272\272-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\345\261\261\346\265\267\344\274\227\347\252\245\351\255\205\344\272\272-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\345\261\261\346\265\267\344\274\227\347\252\245\351\255\205\344\272\272-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\345\261\261\346\265\267\344\274\227\347\252\245\351\255\205\344\272\272-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\345\261\261\351\233\252\351\254\274-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\345\261\261\351\233\252\351\254\274-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\345\261\261\351\233\252\351\254\274-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\345\261\261\351\233\252\351\254\274-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\345\274\247\345\205\211\351\224\213\345\215\253\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\345\274\247\345\205\211\351\224\213\345\215\253\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\345\274\247\345\205\211\351\224\213\345\215\253\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\345\274\247\345\205\211\351\224\213\345\215\253\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\346\211\216\347\275\227\357\274\214\342\200\234\347\213\274\344\271\213\344\270\273\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-A_Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\346\211\216\347\275\227\357\274\214\342\200\234\347\213\274\344\271\213\344\270\273\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-A_Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\346\211\216\347\275\227\357\274\214\342\200\234\347\213\274\344\271\213\344\270\273\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-A_Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\346\211\216\347\275\227\357\274\214\342\200\234\347\213\274\344\271\213\344\270\273\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-A_Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\346\213\263\346\211\213\345\233\232\347\212\257-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_red-x1.webm" "b/src/tools/battlefield_composite/monster_images/\346\213\263\346\211\213\345\233\232\347\212\257-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_red-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\346\213\263\346\211\213\345\233\232\347\212\257-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_red-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\346\213\263\346\211\213\345\233\232\347\212\257-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_red-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\346\217\220\344\272\232\345\215\241\344\271\214\345\245\275\346\210\230\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\346\217\220\344\272\232\345\215\241\344\271\214\345\245\275\346\210\230\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\346\217\220\344\272\232\345\215\241\344\271\214\345\245\275\346\210\230\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\346\217\220\344\272\232\345\215\241\344\271\214\345\245\275\346\210\230\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\346\217\220\344\272\232\345\215\241\344\271\214\347\240\264\345\235\217\347\216\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\346\217\220\344\272\232\345\215\241\344\271\214\347\240\264\345\235\217\347\216\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\346\217\220\344\272\232\345\215\241\344\271\214\347\240\264\345\235\217\347\216\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\346\217\220\344\272\232\345\215\241\344\271\214\347\240\264\345\235\217\347\216\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\346\225\243\345\215\216\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\346\225\243\345\215\216\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\346\225\243\345\215\216\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\346\225\243\345\215\216\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\346\234\227\345\247\206\351\205\222\346\216\250\350\215\220\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\346\234\227\345\247\206\351\205\222\346\216\250\350\215\220\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\346\234\227\345\247\206\351\205\222\346\216\250\350\215\220\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\346\234\227\345\247\206\351\205\222\346\216\250\350\215\220\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\346\234\250\350\243\202\346\210\230\345\243\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\346\234\250\350\243\202\346\210\230\345\243\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\346\234\250\350\243\202\346\210\230\345\243\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\346\234\250\350\243\202\346\210\230\345\243\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\346\235\260\346\226\257\351\241\277\302\267\345\250\201\345\273\211\345\247\206\346\226\257-\351\273\230\350\256\244-\346\210\230\346\226\227-C1_Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\346\235\260\346\226\257\351\241\277\302\267\345\250\201\345\273\211\345\247\206\346\226\257-\351\273\230\350\256\244-\346\210\230\346\226\227-C1_Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\346\235\260\346\226\257\351\241\277\302\267\345\250\201\345\273\211\345\247\206\346\226\257-\351\273\230\350\256\244-\346\210\230\346\226\227-C1_Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\346\235\260\346\226\257\351\241\277\302\267\345\250\201\345\273\211\345\247\206\346\226\257-\351\273\230\350\256\244-\346\210\230\346\226\227-C1_Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\346\237\223\346\261\241\350\272\257\345\243\263-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\346\237\223\346\261\241\350\272\257\345\243\263-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\346\237\223\346\261\241\350\272\257\345\243\263-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\346\237\223\346\261\241\350\272\257\345\243\263-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\346\256\213\345\205\232\350\220\250\345\205\213\346\226\257\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\346\256\213\345\205\232\350\220\250\345\205\213\346\226\257\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\346\256\213\345\205\232\350\220\250\345\205\213\346\226\257\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\346\256\213\345\205\232\350\220\250\345\205\213\346\226\257\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\346\262\270\350\241\200\351\252\221\345\243\253\345\233\242\347\262\276\351\224\220-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\346\262\270\350\241\200\351\252\221\345\243\253\345\233\242\347\262\276\351\224\220-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\346\262\270\350\241\200\351\252\221\345\243\253\345\233\242\347\262\276\351\224\220-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\346\262\270\350\241\200\351\252\221\345\243\253\345\233\242\347\262\276\351\224\220-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\346\263\245\345\262\251\345\267\250\345\203\217-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\346\263\245\345\262\251\345\267\250\345\203\217-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\346\263\245\345\262\251\345\267\250\345\203\217-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\346\263\245\345\262\251\345\267\250\345\203\217-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\346\267\261\346\272\237\350\243\202\347\244\201\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\346\267\261\346\272\237\350\243\202\347\244\201\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\346\267\261\346\272\237\350\243\202\347\244\201\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\346\267\261\346\272\237\350\243\202\347\244\201\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\346\270\251\351\241\272\347\232\204\346\255\246\350\243\205\351\251\256\345\205\275-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\346\270\251\351\241\272\347\232\204\346\255\246\350\243\205\351\251\256\345\205\275-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\346\270\251\351\241\272\347\232\204\346\255\246\350\243\205\351\251\256\345\205\275-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\346\270\251\351\241\272\347\232\204\346\255\246\350\243\205\351\251\256\345\205\275-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\346\271\226\347\225\224\345\277\227\346\204\277\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\346\271\226\347\225\224\345\277\227\346\204\277\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\346\271\226\347\225\224\345\277\227\346\204\277\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\346\271\226\347\225\224\345\277\227\346\204\277\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\346\272\220\347\237\263\347\225\270\345\217\230\344\275\223-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\346\272\220\347\237\263\347\225\270\345\217\230\344\275\223-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\346\272\220\347\237\263\347\225\270\345\217\230\344\275\223-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\346\272\220\347\237\263\347\225\270\345\217\230\344\275\223-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\347\201\260\345\260\276\351\246\231\344\270\273-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\347\201\260\345\260\276\351\246\231\344\270\273-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\347\201\260\345\260\276\351\246\231\344\270\273-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\347\201\260\345\260\276\351\246\231\344\270\273-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\347\202\256\345\207\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Attack-x1.webm" "b/src/tools/battlefield_composite/monster_images/\347\202\256\345\207\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Attack-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\347\202\256\345\207\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Attack-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\347\202\256\345\207\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Attack-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\347\202\256\345\207\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\347\202\256\345\207\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\347\202\256\345\207\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\347\202\256\345\207\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\347\203\210\351\205\222\347\272\247\351\206\222\351\205\222\345\212\251\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\347\203\210\351\205\222\347\272\247\351\206\222\351\205\222\345\212\251\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\347\203\210\351\205\222\347\272\247\351\206\222\351\205\222\345\212\251\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\347\203\210\351\205\222\347\272\247\351\206\222\351\205\222\345\212\251\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\347\213\202\346\232\264\345\256\277\344\270\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\347\213\202\346\232\264\345\256\277\344\270\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\347\213\202\346\232\264\345\256\277\344\270\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\347\213\202\346\232\264\345\256\277\344\270\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\347\213\202\346\232\264\347\232\204\347\214\216\347\213\227pro-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" "b/src/tools/battlefield_composite/monster_images/\347\213\202\346\232\264\347\232\204\347\214\216\347\213\227pro-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\347\213\202\346\232\264\347\232\204\347\214\216\347\213\227pro-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\347\213\202\346\232\264\347\232\204\347\214\216\347\213\227pro-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\347\224\260\351\274\267\345\212\233\345\243\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\347\224\260\351\274\267\345\212\233\345\243\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\347\224\260\351\274\267\345\212\233\345\243\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\347\224\260\351\274\267\345\212\233\345\243\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\347\237\277\350\204\211\345\256\210\345\215\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\347\237\277\350\204\211\345\256\210\345\215\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\347\237\277\350\204\211\345\256\210\345\215\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\347\237\277\350\204\211\345\256\210\345\215\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\347\245\236\345\260\204\346\211\213\345\233\232\347\212\257-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\347\245\236\345\260\204\346\211\213\345\233\232\347\212\257-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\347\245\236\345\260\204\346\211\213\345\233\232\347\212\257-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\347\245\236\345\260\204\346\211\213\345\233\232\347\212\257-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\345\244\247\345\211\221\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" "b/src/tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\345\244\247\345\211\221\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\345\244\247\345\211\221\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\345\244\247\345\211\221\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\345\255\220\350\243\224\351\223\276\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\345\255\220\350\243\224\351\223\276\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\345\255\220\350\243\224\351\223\276\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\345\255\220\350\243\224\351\223\276\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\347\216\213\345\272\255\345\206\233\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\347\216\213\345\272\255\345\206\233\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\347\216\213\345\272\255\345\206\233\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\347\216\213\345\272\255\345\206\233\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\351\200\220\350\205\220\345\205\275-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\351\200\220\350\205\220\345\205\275-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\351\200\220\350\205\220\345\205\275-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\351\200\220\350\205\220\345\205\275-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\351\205\270\346\266\262\346\272\220\347\237\263\350\231\253\302\267\316\261-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" "b/src/tools/battlefield_composite/monster_images/\351\205\270\346\266\262\346\272\220\347\237\263\350\231\253\302\267\316\261-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\351\205\270\346\266\262\346\272\220\347\237\263\350\231\253\302\267\316\261-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\351\205\270\346\266\262\346\272\220\347\237\263\350\231\253\302\267\316\261-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\351\207\215\350\243\205\351\230\262\345\276\241\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" "b/src/tools/battlefield_composite/monster_images/\351\207\215\350\243\205\351\230\262\345\276\241\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\351\207\215\350\243\205\351\230\262\345\276\241\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\351\207\215\350\243\205\351\230\262\345\276\241\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\351\230\265\345\234\260\345\207\273\344\272\272\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\351\230\265\345\234\260\345\207\273\344\272\272\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\351\230\265\345\234\260\345\207\273\344\272\272\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\351\230\265\345\234\260\345\207\273\344\272\272\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\351\243\216\346\203\205\350\241\227\342\200\234\346\230\237\346\234\257\345\270\210\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\351\243\216\346\203\205\350\241\227\342\200\234\346\230\237\346\234\257\345\270\210\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\351\243\216\346\203\205\350\241\227\342\200\234\346\230\237\346\234\257\345\270\210\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\351\243\216\346\203\205\350\241\227\342\200\234\346\230\237\346\234\257\345\270\210\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\351\253\230\345\241\224\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\351\253\230\345\241\224\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\351\253\230\345\241\224\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\351\253\230\345\241\224\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\351\253\230\346\231\256\345\260\274\345\205\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\351\253\230\346\231\256\345\260\274\345\205\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\351\253\230\346\231\256\345\260\274\345\205\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\351\253\230\346\231\256\345\260\274\345\205\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\351\253\230\347\272\247\346\255\246\350\243\205\344\272\272\345\221\230-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\351\253\230\347\272\247\346\255\246\350\243\205\344\272\272\345\221\230-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\351\253\230\347\272\247\346\255\246\350\243\205\344\272\272\345\221\230-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\351\253\230\347\272\247\346\255\246\350\243\205\344\272\272\345\221\230-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/tools/battlefield_composite/monster_images/\351\253\230\350\203\275\346\272\220\347\237\263\350\231\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/src/tools/battlefield_composite/monster_images/\351\253\230\350\203\275\346\272\220\347\237\263\350\231\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "tools/battlefield_composite/monster_images/\351\253\230\350\203\275\346\272\220\347\237\263\350\231\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "src/tools/battlefield_composite/monster_images/\351\253\230\350\203\275\346\272\220\347\237\263\350\231\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git a/tools/battlefield_recognize/class_to_idx.json b/src/tools/battlefield_recognize/class_to_idx.json similarity index 100% rename from tools/battlefield_recognize/class_to_idx.json rename to src/tools/battlefield_recognize/class_to_idx.json diff --git "a/tools/battlefield_recognize/\345\234\272\345\234\260\350\257\206\345\210\253\345\255\227\346\256\265\350\257\264\346\230\216.txt" "b/src/tools/battlefield_recognize/\345\234\272\345\234\260\350\257\206\345\210\253\345\255\227\346\256\265\350\257\264\346\230\216.txt" similarity index 100% rename from "tools/battlefield_recognize/\345\234\272\345\234\260\350\257\206\345\210\253\345\255\227\346\256\265\350\257\264\346\230\216.txt" rename to "src/tools/battlefield_recognize/\345\234\272\345\234\260\350\257\206\345\210\253\345\255\227\346\256\265\350\257\264\346\230\216.txt" diff --git a/tools/convert_model.py b/src/tools/convert_model.py similarity index 70% rename from tools/convert_model.py rename to src/tools/convert_model.py index f55852b..db57d57 100644 --- a/tools/convert_model.py +++ b/src/tools/convert_model.py @@ -1,20 +1,22 @@ import sys + sys.path.append(".") -import predict -from train import UnitAwareTransformer -import predict_onnx +from src.models import predict +from src.models import predict_onnx import numpy as np -from recognize import MONSTER_COUNT +from src.recognition.recognize import MONSTER_COUNT model_path = "models/best_model_full.pth" + def replace_suffix(s): - idx = s.rfind('.') + idx = s.rfind(".") if idx == -1: - return s + '.onnx' + return s + ".onnx" else: - return s[:idx] + '.onnx' + return s[:idx] + ".onnx" + output_path = replace_suffix(model_path) @@ -23,13 +25,13 @@ def replace_suffix(s): model.export_onnx(output_path) # 导出 ONNX 模型 print(f"模型已成功导出为 ONNX 格式,保存路径: {output_path}") -#验证导出结果 +# 验证导出结果 data_array = np.zeros(MONSTER_COUNT * 2, dtype=np.int64) data_array[28] = 16 data_array[MONSTER_COUNT + 30] = 22 left = data_array[:MONSTER_COUNT] right = data_array[MONSTER_COUNT:] -Onnxmodel = predict_onnx.CannotModel(model_path = output_path) +Onnxmodel = predict_onnx.CannotModel(model_path=output_path) prediction = Onnxmodel.get_prediction(left, right) print("预测结果:", prediction) diff --git a/tools/data_cleaning.py b/src/tools/data_cleaning.py similarity index 97% rename from tools/data_cleaning.py rename to src/tools/data_cleaning.py index 98f86c5..dfa5da2 100644 --- a/tools/data_cleaning.py +++ b/src/tools/data_cleaning.py @@ -1,6 +1,8 @@ import pandas as pd import numpy as np +from src.core.paths import PROJECT_ROOT, simulation_path + def clean_data(file_path, output_path): print(f"开始清洗数据文件: {file_path}") @@ -218,6 +220,6 @@ def enhanced_clean(column_data, original_indices, col_idx): if __name__ == "__main__": - input_file = "arknights.csv" - output_file = "arknights_cleaned.csv" + input_file = PROJECT_ROOT / "data" / "raw" / "arknights.csv" + output_file = PROJECT_ROOT / "data" / "processed" / "arknights_cleaned.csv" clean_data(input_file, output_file) diff --git a/tools/data_cleaning_with_field_recognize.py b/src/tools/data_cleaning_with_field_recognize.py similarity index 61% rename from tools/data_cleaning_with_field_recognize.py rename to src/tools/data_cleaning_with_field_recognize.py index 7e1aa72..3b0490d 100644 --- a/tools/data_cleaning_with_field_recognize.py +++ b/src/tools/data_cleaning_with_field_recognize.py @@ -1,6 +1,8 @@ import pandas as pd import numpy as np import os + +from src.core.paths import simulation_path, PROJECT_ROOT import json import re from collections import defaultdict @@ -15,22 +17,22 @@ "altar_vertical": [ {"x": 910, "y": 174, "width": 95, "height": 104}, {"x": 910, "y": 429, "width": 102, "height": 108}, - {"x": 900, "y": 755, "width": 120, "height": 108} + {"x": 900, "y": 755, "width": 120, "height": 108}, ], "block_parallel": [ {"x": 694, "y": 240, "width": 530, "height": 122}, - {"x": 651, "y": 614, "width": 620, "height": 143} + {"x": 651, "y": 614, "width": 620, "height": 143}, ], "block_vertical": [ {"x": 647, "y": 233, "width": 153, "height": 523}, - {"x": 1112, "y": 239, "width": 159, "height": 514} + {"x": 1112, "y": 239, "width": 159, "height": 514}, ], "coil_narrow": [ {"x": 915, "y": 110, "width": 85, "height": 89}, {"x": 815, "y": 257, "width": 86, "height": 98}, {"x": 1024, "y": 258, "width": 79, "height": 98}, {"x": 790, "y": 643, "width": 97, "height": 102}, - {"x": 1031, "y": 639, "width": 102, "height": 108} + {"x": 1031, "y": 639, "width": 102, "height": 108}, ], "coil_wide": [ {"x": 719, "y": 181, "width": 81, "height": 89}, @@ -40,21 +42,15 @@ {"x": 1159, "y": 757, "width": 93, "height": 92}, {"x": 1257, "y": 533, "width": 94, "height": 102}, {"x": 1236, "y": 344, "width": 85, "height": 97}, - {"x": 1120, "y": 180, "width": 75, "height": 91} - ], - "crossbow_top": [ - {"x": 718, "y": 13, "width": 484, "height": 106} - ], - "fire_side_left": [ - {"x": 98, "y": 246, "width": 184, "height": 281} - ], - "fire_side_right": [ - {"x": 1656, "y": 430, "width": 235, "height": 315} + {"x": 1120, "y": 180, "width": 75, "height": 91}, ], + "crossbow_top": [{"x": 718, "y": 13, "width": 484, "height": 106}], + "fire_side_left": [{"x": 98, "y": 246, "width": 184, "height": 281}], + "fire_side_right": [{"x": 1656, "y": 430, "width": 235, "height": 315}], "fire_top": [ {"x": 532, "y": 17, "width": 188, "height": 97}, - {"x": 1325, "y": 14, "width": 60, "height": 100} - ] + {"x": 1325, "y": 14, "width": 60, "height": 100}, + ], } @@ -73,19 +69,26 @@ def softmax(x: np.ndarray) -> np.ndarray: return e_x / e_x.sum(axis=-1, keepdims=True) -def predict_scene(session: ort.InferenceSession, idx_to_class: dict, image_path: str, threshold: float = 0.5) -> list[ - str]: +def predict_scene( + session: ort.InferenceSession, + idx_to_class: dict, + image_path: str, + threshold: float = 0.5, +) -> list[str]: try: - full_image = Image.open(image_path).convert('RGB') + full_image = Image.open(image_path).convert("RGB") except Exception: return [] if full_image.size != (1920, 1080): return [] - input_name, output_name = session.get_inputs()[0].name, session.get_outputs()[0].name + input_name, output_name = ( + session.get_inputs()[0].name, + session.get_outputs()[0].name, + ) detected_classes = [] for location, boxes in ROI_COORDINATES.items(): for i, box in enumerate(boxes): - x, y, w, h = box['x'], box['y'], box['width'], box['height'] + x, y, w, h = box["x"], box["y"], box["width"], box["height"] roi_pil = full_image.crop((x, y, x + w, y + h)) input_tensor = preprocess_pil_image(roi_pil) outputs = session.run([output_name], {input_name: input_tensor}) @@ -94,7 +97,7 @@ def predict_scene(session: ort.InferenceSession, idx_to_class: dict, image_path: predicted_index = np.argmax(probabilities) if probabilities[predicted_index] >= threshold: predicted_class = idx_to_class[predicted_index] - if not predicted_class.endswith('_none'): + if not predicted_class.endswith("_none"): detected_classes.append(predicted_class) return detected_classes @@ -103,14 +106,21 @@ def predict_scene(session: ort.InferenceSession, idx_to_class: dict, image_path: # SECTION 2: 数据清洗主模块 (修改了最后的合并与保存部分) # ============================================================================== -def clean_data(file_path, output_path, screenshots_base_path, onnx_model_path, class_map_path): + +def clean_data( + file_path, + output_path, + screenshots_base_path, + onnx_model_path, + class_map_path, +): print(f"开始清洗数据文件: {file_path}") try: data = pd.read_csv(file_path, header=0) except FileNotFoundError: print(f"错误: 找不到数据文件 '{file_path}'") return - data['original_index'] = data.index + 1 + data["original_index"] = data.index + 1 # --- 原始清洗逻辑部分 (无变更) --- features = data.iloc[:, :-3] labels = data.iloc[:, -3] @@ -119,36 +129,53 @@ def clean_data(file_path, output_path, screenshots_base_path, onnx_model_path, c # ... (其余清洗逻辑与原脚本完全相同) last_row_features = features.iloc[-1].values last_row_valid = True - if abs(last_row_features[27]) > 6 or abs(last_row_features[61]) > 6: last_row_valid = False - if np.any(np.abs(last_row_features) >= 100): last_row_valid = False - if not last_row_valid: print("错误: 最后一行不满足清洗条件"); return + if abs(last_row_features[27]) > 6 or abs(last_row_features[61]) > 6: + last_row_valid = False + if np.any(np.abs(last_row_features) >= 100): + last_row_valid = False + if not last_row_valid: + print("错误: 最后一行不满足清洗条件") + return last_row = data.iloc[-1].copy() - rows_to_remove = [i for i, row in enumerate(features.values) if np.any(np.abs(row) >= 100)] + rows_to_remove = [ + i + for i, row in enumerate(features.values) + if np.any(np.abs(row) >= 100) + ] cleaned_data = data.drop(rows_to_remove).reset_index(drop=True) - if not (len(data) - 1 in rows_to_remove): cleaned_data = cleaned_data.iloc[:-1] - if rows_to_remove: cleaned_data = pd.concat([cleaned_data, pd.DataFrame([last_row] * len(rows_to_remove))], - ignore_index=True) - cleaned_data = cleaned_data.drop_duplicates(subset=cleaned_data.columns[:-3], keep='first').reset_index(drop=True) - features_cleaned, labels_cleaned, pic_names_cleaned = cleaned_data.iloc[:, :-3], cleaned_data.iloc[:, - -3], cleaned_data.iloc[:, -2] + if not (len(data) - 1 in rows_to_remove): + cleaned_data = cleaned_data.iloc[:-1] + if rows_to_remove: + cleaned_data = pd.concat( + [cleaned_data, pd.DataFrame([last_row] * len(rows_to_remove))], + ignore_index=True, + ) + cleaned_data = cleaned_data.drop_duplicates( + subset=cleaned_data.columns[:-3], keep="first" + ).reset_index(drop=True) + features_cleaned, labels_cleaned, pic_names_cleaned = ( + cleaned_data.iloc[:, :-3], + cleaned_data.iloc[:, -3], + cleaned_data.iloc[:, -2], + ) # ... (异常波动筛选逻辑完全相同) # --- 画面元素识别集成部分 (无变更) --- print("\n开始识别截图中的游戏元素...") try: session = ort.InferenceSession(onnx_model_path) - with open(class_map_path, 'r', encoding='utf-8') as f: + with open(class_map_path, "r", encoding="utf-8") as f: class_to_idx = json.load(f) idx_to_class = {v: k for k, v in class_to_idx.items()} except Exception as e: - print(f"错误:加载模型或class_map文件失败: {e}"); + print(f"错误:加载模型或class_map文件失败: {e}") return grouped_elements = defaultdict(list) for class_name in class_to_idx.keys(): - if class_name.endswith('_none'): + if class_name.endswith("_none"): continue - condensed_name = re.sub(r'_left_', '_', class_name) - condensed_name = re.sub(r'_right_', '_', condensed_name) + condensed_name = re.sub(r"_left_", "_", class_name) + condensed_name = re.sub(r"_right_", "_", condensed_name) grouped_elements[condensed_name].append(class_name) image_feature_columns = sorted(grouped_elements.keys()) print(f"将聚合生成 {len(image_feature_columns)} 个新特征列。") @@ -162,14 +189,20 @@ def clean_data(file_path, output_path, screenshots_base_path, onnx_model_path, c all_rows_image_data.append(row_image_data) continue try: - detected_full_names = set(predict_scene(session, idx_to_class, image_path, threshold=0.5)) + detected_full_names = set( + predict_scene(session, idx_to_class, image_path, threshold=0.5) + ) row_image_data = {} for condensed_name, full_names in grouped_elements.items(): num_positions = len(full_names) if num_positions == 1: - row_image_data[condensed_name] = 1 if full_names[0] in detected_full_names else 0 + row_image_data[condensed_name] = ( + 1 if full_names[0] in detected_full_names else 0 + ) else: - detections_in_group = [fn in detected_full_names for fn in full_names] + detections_in_group = [ + fn in detected_full_names for fn in full_names + ] num_detected = sum(detections_in_group) if num_detected == num_positions: row_image_data[condensed_name] = 1 @@ -192,7 +225,9 @@ def clean_data(file_path, output_path, screenshots_base_path, onnx_model_path, c # 1. 检查并拆分155个原始特征为L(77)和R(78)两组 if features_cleaned.shape[1] != 122: - print(f"警告: 期望122个原始特征,但检测到{features_cleaned.shape[1]}个。将按前61列和剩余列进行分割。") + print( + f"警告: 期望122个原始特征,但检测到{features_cleaned.shape[1]}个。将按前61列和剩余列进行分割。" + ) features_L = features_cleaned.iloc[:, :61] features_R = features_cleaned.iloc[:, 61:] @@ -202,29 +237,43 @@ def clean_data(file_path, output_path, screenshots_base_path, onnx_model_path, c num_r_features = features_R.shape[1] headers_L = [f"{i}L" for i in range(1, 62)] # 1L to 77L - headers_elements_L = [f"{i}L" for i in range(62, 62 + num_element_features)] + headers_elements_L = [ + f"{i}L" for i in range(62, 62 + num_element_features) + ] headers_R = [f"{i}R" for i in range(1, num_r_features + 1)] # 1R to 78R - headers_elements_R = [f"{i}R" for i in range(num_r_features + 1, num_r_features + 1 + num_element_features)] + headers_elements_R = [ + f"{i}R" + for i in range( + num_r_features + 1, num_r_features + 1 + num_element_features + ) + ] # 最终表头顺序 - final_headers = (headers_L + headers_elements_L + - headers_R + headers_elements_R + - ['Result', 'ImgPath']) + final_headers = ( + headers_L + + headers_elements_L + + headers_R + + headers_elements_R + + ["Result", "ImgPath"] + ) # 3. 为Series命名,以便在拼接时作为列名 - labels_cleaned.name = 'Result' - pic_names_cleaned.name = 'ImgPath' + labels_cleaned.name = "Result" + pic_names_cleaned.name = "ImgPath" # 4. 按照新的顺序拼接所有数据部分 - final_cleaned_data = pd.concat([ - features_L, # 1L-61L - image_data_df, # 12个元素特征 - features_R, # 1R-61R - image_data_df.copy(), # 12个元素特征 (副本) - labels_cleaned, # label - pic_names_cleaned # screenshot_filename - ], axis=1) + final_cleaned_data = pd.concat( + [ + features_L, # 1L-61L + image_data_df, # 12个元素特征 + features_R, # 1R-61R + image_data_df.copy(), # 12个元素特征 (副本) + labels_cleaned, # label + pic_names_cleaned, # screenshot_filename + ], + axis=1, + ) # 5. 将新生成的表头赋予DataFrame final_cleaned_data.columns = final_headers @@ -233,17 +282,25 @@ def clean_data(file_path, output_path, screenshots_base_path, onnx_model_path, c final_cleaned_data.to_csv(output_path, index=False, header=True) print(f"\n清洗和识别后的数据已保存到: {output_path}") - print(f"最终数据维度: {final_cleaned_data.shape[0]} 行, {final_cleaned_data.shape[1]} 列") + print( + f"最终数据维度: {final_cleaned_data.shape[0]} 行, {final_cleaned_data.shape[1]} 列" + ) print(f"已按要求生成自定义表头。") if __name__ == "__main__": # 路径配置与之前保持一致 - input_file = r"arknights.csv" - output_file = r"arknights_with_field_recognize_v2.csv" - screenshots_base_path = r"images" - - model_dir = r"battlefield_recognize" - onnx_model_path = os.path.join(model_dir, 'field_recognize.onnx') - class_map_path = os.path.join(model_dir, 'class_to_idx.json') - clean_data(input_file, output_file, screenshots_base_path, onnx_model_path, class_map_path) \ No newline at end of file + input_file = PROJECT_ROOT / "data" / "raw" / "arknights.csv" + output_file = PROJECT_ROOT / "data" / "processed" / "arknights_with_field_recognize_v2.csv" + screenshots_base_path = PROJECT_ROOT / "data" / "images" + + model_dir = PROJECT_ROOT / "models" / "battlefield_recognize" + onnx_model_path = os.path.join(model_dir, "field_recognize.onnx") + class_map_path = os.path.join(model_dir, "class_to_idx.json") + clean_data( + input_file, + output_file, + screenshots_base_path, + onnx_model_path, + class_map_path, + ) diff --git a/tools/data_cleaning_with_field_recognize_gpu.py b/src/tools/data_cleaning_with_field_recognize_gpu.py similarity index 61% rename from tools/data_cleaning_with_field_recognize_gpu.py rename to src/tools/data_cleaning_with_field_recognize_gpu.py index 13ae6af..2f12699 100644 --- a/tools/data_cleaning_with_field_recognize_gpu.py +++ b/src/tools/data_cleaning_with_field_recognize_gpu.py @@ -9,6 +9,8 @@ import torch.nn as nn from torchvision import models, transforms +from src.core.paths import simulation_path, PROJECT_ROOT + # ============================================================================== # SECTION 1: 游戏画面元素识别模块 (已修改为PyTorch+GPU) # ============================================================================== @@ -17,22 +19,22 @@ "altar_vertical": [ {"x": 910, "y": 174, "width": 95, "height": 104}, {"x": 910, "y": 429, "width": 102, "height": 108}, - {"x": 900, "y": 755, "width": 120, "height": 108} + {"x": 900, "y": 755, "width": 120, "height": 108}, ], "block_parallel": [ {"x": 694, "y": 240, "width": 530, "height": 122}, - {"x": 651, "y": 614, "width": 620, "height": 143} + {"x": 651, "y": 614, "width": 620, "height": 143}, ], "block_vertical": [ {"x": 647, "y": 233, "width": 153, "height": 523}, - {"x": 1112, "y": 239, "width": 159, "height": 514} + {"x": 1112, "y": 239, "width": 159, "height": 514}, ], "coil_narrow": [ {"x": 915, "y": 110, "width": 85, "height": 89}, {"x": 815, "y": 257, "width": 86, "height": 98}, {"x": 1024, "y": 258, "width": 79, "height": 98}, {"x": 790, "y": 643, "width": 97, "height": 102}, - {"x": 1031, "y": 639, "width": 102, "height": 108} + {"x": 1031, "y": 639, "width": 102, "height": 108}, ], "coil_wide": [ {"x": 719, "y": 181, "width": 81, "height": 89}, @@ -42,35 +44,29 @@ {"x": 1159, "y": 757, "width": 93, "height": 92}, {"x": 1257, "y": 533, "width": 94, "height": 102}, {"x": 1236, "y": 344, "width": 85, "height": 97}, - {"x": 1120, "y": 180, "width": 75, "height": 91} - ], - "crossbow_top": [ - {"x": 718, "y": 13, "width": 484, "height": 106} - ], - "fire_side_left": [ - {"x": 98, "y": 246, "width": 184, "height": 281} - ], - "fire_side_right": [ - {"x": 1656, "y": 430, "width": 235, "height": 315} + {"x": 1120, "y": 180, "width": 75, "height": 91}, ], + "crossbow_top": [{"x": 718, "y": 13, "width": 484, "height": 106}], + "fire_side_left": [{"x": 98, "y": 246, "width": 184, "height": 281}], + "fire_side_right": [{"x": 1656, "y": 430, "width": 235, "height": 315}], "fire_top": [ {"x": 532, "y": 17, "width": 188, "height": 97}, - {"x": 1325, "y": 14, "width": 60, "height": 100} - ] + {"x": 1325, "y": 14, "width": 60, "height": 100}, + ], } def predict_scene_pytorch( - model: nn.Module, - idx_to_class: dict, - image_path: str, - transform: transforms.Compose, - device: torch.device, - threshold: float = 0.5 + model: nn.Module, + idx_to_class: dict, + image_path: str, + transform: transforms.Compose, + device: torch.device, + threshold: float = 0.5, ) -> list[str]: """使用PyTorch模型对图片进行场景识别(GPU加速版)""" try: - full_image = Image.open(image_path).convert('RGB') + full_image = Image.open(image_path).convert("RGB") except Exception: return [] @@ -81,7 +77,7 @@ def predict_scene_pytorch( with torch.no_grad(): for location, boxes in ROI_COORDINATES.items(): for i, box in enumerate(boxes): - x, y, w, h = box['x'], box['y'], box['width'], box['height'] + x, y, w, h = box["x"], box["y"], box["width"], box["height"] roi_pil = full_image.crop((x, y, x + w, y + h)) input_tensor = transform(roi_pil).unsqueeze(0).to(device) outputs = model(input_tensor) @@ -91,7 +87,7 @@ def predict_scene_pytorch( if max_prob.item() >= threshold: predicted_class = idx_to_class[predicted_index] - if not predicted_class.endswith('_none'): + if not predicted_class.endswith("_none"): detected_classes.append(predicted_class) return detected_classes @@ -100,14 +96,21 @@ def predict_scene_pytorch( # SECTION 2: 数据清洗主模块 (修改了最后的合并与保存部分) # ============================================================================== -def clean_data(file_path, output_path, screenshots_base_path, onnx_model_path, class_map_path): + +def clean_data( + file_path, + output_path, + screenshots_base_path, + onnx_model_path, + class_map_path, +): print(f"开始清洗数据文件: {file_path}") try: data = pd.read_csv(file_path, header=0) except FileNotFoundError: print(f"错误: 找不到数据文件 '{file_path}'") return - data['original_index'] = data.index + 1 + data["original_index"] = data.index + 1 # --- 原始清洗逻辑部分 (无变更) --- features = data.iloc[:, :-3] @@ -117,18 +120,35 @@ def clean_data(file_path, output_path, screenshots_base_path, onnx_model_path, c # ... (其余清洗逻辑与原脚本完全相同) last_row_features = features.iloc[-1].values last_row_valid = True - if abs(last_row_features[27]) > 6 or abs(last_row_features[61]) > 6: last_row_valid = False - if np.any(np.abs(last_row_features) >= 100): last_row_valid = False - if not last_row_valid: print("错误: 最后一行不满足清洗条件"); return + if abs(last_row_features[27]) > 6 or abs(last_row_features[61]) > 6: + last_row_valid = False + if np.any(np.abs(last_row_features) >= 100): + last_row_valid = False + if not last_row_valid: + print("错误: 最后一行不满足清洗条件") + return last_row = data.iloc[-1].copy() - rows_to_remove = [i for i, row in enumerate(features.values) if np.any(np.abs(row) >= 100)] + rows_to_remove = [ + i + for i, row in enumerate(features.values) + if np.any(np.abs(row) >= 100) + ] cleaned_data = data.drop(rows_to_remove).reset_index(drop=True) - if not (len(data) - 1 in rows_to_remove): cleaned_data = cleaned_data.iloc[:-1] - if rows_to_remove: cleaned_data = pd.concat([cleaned_data, pd.DataFrame([last_row] * len(rows_to_remove))], - ignore_index=True) - cleaned_data = cleaned_data.drop_duplicates(subset=cleaned_data.columns[:-3], keep='first').reset_index(drop=True) - features_cleaned, labels_cleaned, pic_names_cleaned = cleaned_data.iloc[:, :-3], cleaned_data.iloc[:, - -3], cleaned_data.iloc[:, -2] + if not (len(data) - 1 in rows_to_remove): + cleaned_data = cleaned_data.iloc[:-1] + if rows_to_remove: + cleaned_data = pd.concat( + [cleaned_data, pd.DataFrame([last_row] * len(rows_to_remove))], + ignore_index=True, + ) + cleaned_data = cleaned_data.drop_duplicates( + subset=cleaned_data.columns[:-3], keep="first" + ).reset_index(drop=True) + features_cleaned, labels_cleaned, pic_names_cleaned = ( + cleaned_data.iloc[:, :-3], + cleaned_data.iloc[:, -3], + cleaned_data.iloc[:, -2], + ) # ... (异常波动筛选逻辑完全相同) # --- 画面元素识别集成部分 (修改为PyTorch+GPU) --- @@ -138,11 +158,11 @@ def clean_data(file_path, output_path, screenshots_base_path, onnx_model_path, c try: # 从ONNX路径生成对应的PTH路径 - pth_model_path = onnx_model_path.replace('.onnx', '.pth') + pth_model_path = onnx_model_path.replace(".onnx", ".pth") print(f"加载PyTorch模型: {pth_model_path}") # 加载类别映射 - with open(class_map_path, 'r', encoding='utf-8') as f: + with open(class_map_path, "r", encoding="utf-8") as f: class_to_idx = json.load(f) idx_to_class = {v: k for k, v in class_to_idx.items()} num_classes = len(class_to_idx) @@ -156,11 +176,15 @@ def clean_data(file_path, output_path, screenshots_base_path, onnx_model_path, c model.eval() # 定义图像转换 - transform = transforms.Compose([ - transforms.Resize((224, 224)), - transforms.ToTensor(), - transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) - ]) + transform = transforms.Compose( + [ + transforms.Resize((224, 224)), + transforms.ToTensor(), + transforms.Normalize( + mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225] + ), + ] + ) # 重新定义predict_scene函数使用PyTorch模型 def predict_scene(session, idx_to_class, image_path, threshold=0.5): @@ -174,10 +198,10 @@ def predict_scene(session, idx_to_class, image_path, threshold=0.5): grouped_elements = defaultdict(list) for class_name in class_to_idx.keys(): - if class_name.endswith('_none'): + if class_name.endswith("_none"): continue - condensed_name = re.sub(r'_left_', '_', class_name) - condensed_name = re.sub(r'_right_', '_', condensed_name) + condensed_name = re.sub(r"_left_", "_", class_name) + condensed_name = re.sub(r"_right_", "_", condensed_name) grouped_elements[condensed_name].append(class_name) image_feature_columns = sorted(grouped_elements.keys()) print(f"将聚合生成 {len(image_feature_columns)} 个新特征列。") @@ -191,14 +215,20 @@ def predict_scene(session, idx_to_class, image_path, threshold=0.5): all_rows_image_data.append(row_image_data) continue try: - detected_full_names = set(predict_scene(None, idx_to_class, image_path, threshold=0.5)) + detected_full_names = set( + predict_scene(None, idx_to_class, image_path, threshold=0.5) + ) row_image_data = {} for condensed_name, full_names in grouped_elements.items(): num_positions = len(full_names) if num_positions == 1: - row_image_data[condensed_name] = 1 if full_names[0] in detected_full_names else 0 + row_image_data[condensed_name] = ( + 1 if full_names[0] in detected_full_names else 0 + ) else: - detections_in_group = [fn in detected_full_names for fn in full_names] + detections_in_group = [ + fn in detected_full_names for fn in full_names + ] num_detected = sum(detections_in_group) if num_detected == num_positions: row_image_data[condensed_name] = 1 @@ -221,7 +251,9 @@ def predict_scene(session, idx_to_class, image_path, threshold=0.5): # 1. 检查并拆分155个原始特征为L(77)和R(78)两组 if features_cleaned.shape[1] != 122: - print(f"警告: 期望122个原始特征,但检测到{features_cleaned.shape[1]}个。将按前61列和剩余列进行分割。") + print( + f"警告: 期望122个原始特征,但检测到{features_cleaned.shape[1]}个。将按前61列和剩余列进行分割。" + ) features_L = features_cleaned.iloc[:, :61] features_R = features_cleaned.iloc[:, 61:] @@ -231,29 +263,43 @@ def predict_scene(session, idx_to_class, image_path, threshold=0.5): num_r_features = features_R.shape[1] headers_L = [f"{i}L" for i in range(1, 62)] # 1L to 61L - headers_elements_L = [f"{i}L" for i in range(62, 62 + num_element_features)] + headers_elements_L = [ + f"{i}L" for i in range(62, 62 + num_element_features) + ] headers_R = [f"{i}R" for i in range(1, num_r_features + 1)] # 1R to 61R - headers_elements_R = [f"{i}R" for i in range(num_r_features + 1, num_r_features + 1 + num_element_features)] + headers_elements_R = [ + f"{i}R" + for i in range( + num_r_features + 1, num_r_features + 1 + num_element_features + ) + ] # 最终表头顺序 - final_headers = (headers_L + headers_elements_L + - headers_R + headers_elements_R + - ['Result', 'ImgPath']) + final_headers = ( + headers_L + + headers_elements_L + + headers_R + + headers_elements_R + + ["Result", "ImgPath"] + ) # 3. 为Series命名,以便在拼接时作为列名 - labels_cleaned.name = 'Result' - pic_names_cleaned.name = 'ImgPath' + labels_cleaned.name = "Result" + pic_names_cleaned.name = "ImgPath" # 4. 按照新的顺序拼接所有数据部分 - final_cleaned_data = pd.concat([ - features_L, # 1L-61L - image_data_df, # 元素特征 - features_R, # 1R-61R - image_data_df.copy(), # 元素特征 (副本) - labels_cleaned, # label - pic_names_cleaned # screenshot_filename - ], axis=1) + final_cleaned_data = pd.concat( + [ + features_L, # 1L-61L + image_data_df, # 元素特征 + features_R, # 1R-61R + image_data_df.copy(), # 元素特征 (副本) + labels_cleaned, # label + pic_names_cleaned, # screenshot_filename + ], + axis=1, + ) # 5. 将新生成的表头赋予DataFrame final_cleaned_data.columns = final_headers @@ -262,24 +308,34 @@ def predict_scene(session, idx_to_class, image_path, threshold=0.5): final_cleaned_data.to_csv(output_path, index=False, header=True) print(f"\n清洗和识别后的数据已保存到: {output_path}") - print(f"最终数据维度: {final_cleaned_data.shape[0]} 行, {final_cleaned_data.shape[1]} 列") + print( + f"最终数据维度: {final_cleaned_data.shape[0]} 行, {final_cleaned_data.shape[1]} 列" + ) print(f"已按要求生成自定义表头。") if __name__ == "__main__": # 路径配置与之前保持一致 - input_file = r"arknights.csv" - output_file = r"arknights_with_field_recognize_v2.csv" - screenshots_base_path = r"images" + input_file = PROJECT_ROOT / "data" / "raw" / "arknights.csv" + output_file = PROJECT_ROOT / "data" / "processed" / "arknights_with_field_recognize_v2.csv" + screenshots_base_path = PROJECT_ROOT / "data" / "images" - model_dir = r"battlefield_recognize" - onnx_model_path = os.path.join(model_dir, 'field_recognize.onnx') - class_map_path = os.path.join(model_dir, 'class_to_idx.json') + model_dir = PROJECT_ROOT / "models" / "battlefield_recognize" + onnx_model_path = os.path.join(model_dir, "field_recognize.onnx") + class_map_path = os.path.join(model_dir, "class_to_idx.json") # 自动检查PTH文件是否存在 - pth_model_path = onnx_model_path.replace('.onnx', '.pth') + pth_model_path = onnx_model_path.replace(".onnx", ".pth") if not os.path.exists(pth_model_path): print(f"错误: 找不到对应的PTH模型文件 '{pth_model_path}'") - print(f"请确保存在与ONNX同名的PTH文件: {os.path.basename(pth_model_path)}") + print( + f"请确保存在与ONNX同名的PTH文件: {os.path.basename(pth_model_path)}" + ) else: - clean_data(input_file, output_file, screenshots_base_path, onnx_model_path, class_map_path) \ No newline at end of file + clean_data( + input_file, + output_file, + screenshots_base_path, + onnx_model_path, + class_map_path, + ) diff --git a/tools/data_nanWriter.py b/src/tools/data_nanWriter.py similarity index 94% rename from tools/data_nanWriter.py rename to src/tools/data_nanWriter.py index ddd6ad4..0dc164a 100644 --- a/tools/data_nanWriter.py +++ b/src/tools/data_nanWriter.py @@ -2,12 +2,13 @@ import torch import numpy as np from tqdm import tqdm -from train import UnitAwareTransformer +from src.models.train import UnitAwareTransformer +from src.core.paths import simulation_path # 配置参数(需要与训练时一致) CONFIG = { - "csv_path": "arknights.csv", - "model_path": "models/best_model_full.pth", + "csv_path": str(PROJECT_ROOT / "data" / "raw" / "arknights.csv"), + "model_path": str(PROJECT_ROOT / "models" / "best_model_full.pth"), "max_feature_value": 300, "embed_dim": 128, "num_heads": 8, diff --git a/src/tools/data_washer_new.py b/src/tools/data_washer_new.py new file mode 100644 index 0000000..cf1ef85 --- /dev/null +++ b/src/tools/data_washer_new.py @@ -0,0 +1,1383 @@ +import logging +from pathlib import Path +import cv2 +import numpy as np +import csv +import os +import sys +from src.recognition import recognize +import tqdm + +MONSTER_NUM = 56 +black_list_rows = [] + + +def merge(nums): + if not nums: + return "" + intervals = [] + start = end = nums[0] + for num in nums[1:]: + if num == end + 1: + end = num + else: + intervals.append((start, end)) + start = end = num + intervals.append((start, end)) # 添加最后一个区间 + + parts = [] + for s, e in intervals: + if s == e: + parts.append(str(s)) + else: + parts.append(f"{s}-{e}") + return ",".join(parts) + + +def is_continuous_sublist(sub, main): + return any( + sub == main[i : i + len(sub)] for i in range(len(main) - len(sub) + 1) + ) + + +def remove_duplicate_subsequences_easy(listdata, threshold=3): + record = [] + for i in range(len(listdata) - threshold - 1): + if is_continuous_sublist( + listdata[i + 1 : i + threshold + 2], listdata[: i + threshold + 1] + ): + record.extend(list(range(i + 1, i + threshold + 2))) + reco_last = list(set(record)) + reco_last.sort() + processed_data = [ + listdata[j] for j in range(len(listdata)) if j not in reco_last + ] + return processed_data, reco_last + + +def remove_duplicate_subsequences(arr, threshold=3): + """ + 处理二维数组版本,将每行视为独立元素,避免内存溢出 + :param arr: 二维np数组,形状为(N, D) + :param threshold: 需要删除的连续重复子序列最小长度 + :return: 去重后的数组,被删除的索引列表 + """ + if arr.ndim != 2: + raise ValueError("输入必须是二维数组") + + n = arr.shape[0] + print(arr[9]) + # 哈希化每行以便快速比较 + dtype = np.dtype((np.void, arr.dtype.itemsize * arr.shape[1])) + hashed_arr = np.ascontiguousarray(arr).view(dtype).flatten() + + # 初始化滚动数组和列最大值记录 + prev_row = np.zeros(n, dtype=int) + max_per_col = np.zeros(n, dtype=int) + targ = 10 + for i in range(n): + # 生成当前行比较掩码 + equal_mask = hashed_arr[i] == hashed_arr + + # 计算当前行DP值 + curr_row = np.zeros(n, dtype=int) + curr_row[0] = equal_mask[0] # 处理j=0 + + if i > 0: + # 向量化计算j>=1的情况 + curr_row[1:] = np.where(equal_mask[1:], prev_row[:-1] + 1, 0) + + # 更新列最大值(只考虑i<=j的情况) + col_mask = np.arange(n) > i + max_per_col = np.maximum(max_per_col, curr_row * col_mask) + + # 滚动更新 + prev_row = curr_row + if i * 100 / n >= targ: + print("检查重复数据,处理进度: {:.1f}%: ".format(i * 100 / n)) + targ += 10 + + # 确定有效列并生成删除索引 + valid_cols = np.where(max_per_col >= threshold)[0] + to_remove = set() + + for j in valid_cols: + length = max_per_col[j] + start = max(0, j - length) + to_remove.update(range(start, j + 1)) + + final_indices = sorted(to_remove) + return np.delete(arr, final_indices, axis=0), final_indices + + +def isfloat(value): + try: + float(value) + return True + except ValueError: + return False + + +def read_and_remove_zeros(filename, MONSTER_NUM=56): + """ + 输入数据文件名 + 输出去0和空之后的数组,和删去的行号列表 + """ + data = [] + datafull = [] + row_id = 0 + kong = [] + short = [] + lines_num = MONSTER_NUM * 2 + with open(filename, "r") as file: + csv_reader = csv.reader(file) + for row in csv_reader: + if len(row) < lines_num + 1: + short.append(row_id) + data.append([0] * lines_num) + datafull.append([0] * lines_num) + # 分离数字部分和末尾的字母 + elif isfloat(row[0]) and "" not in row[:lines_num]: + numbers = list( + map(int, map(float, row[:lines_num])) + ) # 转换为整数列表 + vals = row[lines_num:] + datafull.append(numbers + vals) + data.append(numbers) + else: + kong.append(row_id) + data.append([0] * lines_num) + datafull.append([0] * lines_num) + # 把这一行转化为全0行暂时录入 + row_id += 1 + print("原数据总长度:", len(datafull)) + print("数据长度过短的行:", merge(short)) + print("含有不合法数据的行:", merge(kong)) + + np_array = np.array(data) + # 去除全零行 + all_zeros = [] + for i in range(np_array.shape[0]): + if np.all(np_array[i][MONSTER_NUM:] == 0) or np.all( + np_array[i][:MONSTER_NUM] == 0 + ): + all_zeros.append(i) + # np.delete(np_array, all_zeros, axis=0) + + data_new = [ + datafull[j] for j in range(len(datafull)) if j not in all_zeros + ] + + all_zeros_idx = [i for i in all_zeros if i not in kong + short] + print("一侧数据全为0的行:", merge(all_zeros_idx)) + print("筛选后数据总长度:", len(data_new)) + return data_new, all_zeros, len(datafull) + + +def do_duplicate(listdata): + if listdata == []: + return [], [] + num_data = [ + list(map(int, map(float, i[: MONSTER_NUM * 2]))) for i in listdata + ] + np_num_data = np.array(num_data) + _, remove_list = remove_duplicate_subsequences(np_num_data, threshold=3) + # print(remove_list) + result = [ + listdata[j] for j in range(len(listdata)) if j not in remove_list + ] + return result, remove_list + + +def ori_pos(n, del1, del2): + remaining_after_first = [i for i in range(n) if i not in del1] + second_deleted_original = [remaining_after_first[t] for t in del2] + all_deleted = del1 + second_deleted_original + all_deleted.sort() + return all_deleted, second_deleted_original + + +def view_monster_counts(listdata): + if listdata == []: + return True, [], [] + wrong_counts = [] + num_left = [list(map(int, map(float, i[:MONSTER_NUM]))) for i in listdata] + num_right = [ + list(map(int, map(float, i[MONSTER_NUM : MONSTER_NUM * 2]))) + for i in listdata + ] + print(len(num_left[0]), len(num_right[0])) + black_listed = False + MONSTER_MIN = 0 + MONSTER_MAX = 100 + MONSTER_LIMIT = { + 0: [range(0, 100), "狗", False], + 1: [range(0, 50), "红虫", False], + 2: [range(0, 30), "大盾", False], + 3: [range(0, 30), "大剑", False], + 6: [range(0, 6), "庞贝", False], + 8: [range(0, 4), "石头人", False], + 27: [range(0, 4), "杰斯顿", False], + 28: [[0], "自在", True], + 29: [[0], "狼主", True], + 30: [[0], "雷德", True], # 三大boss全设为0 + } + ind = 0 + for i1, i2 in zip(num_left, num_right): + for x1 in i1: + if x1 < MONSTER_MIN: + print(f"{ind}行左侧发现小于0的数据!") + if ind not in wrong_counts: + wrong_counts.append(ind) + if x1 > MONSTER_MAX: + print(f"{ind}行左侧发现大于100的数据!") + if ind not in wrong_counts: + wrong_counts.append(ind) + for x2 in i2: + if x2 < MONSTER_MIN: + print(f"{ind}行右侧发现小于0的数据!") + if ind not in wrong_counts: + wrong_counts.append(ind) + if x2 > MONSTER_MAX: + print(f"{ind}行右侧发现大于100的数据!") + if ind not in wrong_counts: + wrong_counts.append(ind) + for j in MONSTER_LIMIT: + if i1[j] not in MONSTER_LIMIT[j][0]: + print(f"{ind}行左侧发现{MONSTER_LIMIT[j][1]},数量:{i1[j]}") + if ind not in wrong_counts: + wrong_counts.append(ind) + if MONSTER_LIMIT[j][2] and not black_listed: + black_listed = True + print(f"确认为30人数据,文档加入黑名单。") + if i2[j] not in MONSTER_LIMIT[j][0]: + print(f"{ind}行右侧发现{MONSTER_LIMIT[j][1]},数量:{i2[j]}") + if ind not in wrong_counts: + wrong_counts.append(ind) + if MONSTER_LIMIT[j][2] and not black_listed: + black_listed = True + print(f"确认为30人数据,文档加入黑名单。") + ind += 1 + processed_data = [ + listdata[j] for j in range(len(listdata)) if j not in wrong_counts + ] + mwdata = is_list_true_np(processed_data) + processed_data2 = [ + processed_data[j] + for j in range(len(processed_data)) + if j not in mwdata + ] + print(f"怪物信息不符合权重分配的数据行:{mwdata}") + return black_listed, wrong_counts, mwdata, processed_data2 + + +def del_duplicate_by_time(listdata, delete_no_time=True): + ind = 0 + no_time = [] + timedata = [] + wrong_time = [] + for i in listdata: + if len(i) < MONSTER_NUM * 2 + 2 or i[-1] == "N/A": + no_time.append(ind) + ind += 1 + print(merge(no_time), "行:未发现时间戳!") + data_with_time = [ + listdata[j] for j in range(len(listdata)) if j not in no_time + ] + ind = 0 + for i in data_with_time: + if i[-1] not in timedata: + timedata.append(i[-1]) + else: + wrong_time.append(ind) + print( + f"{timedata.index(i[-1])}行与{ind}行发现同名截图,文件名:{i[-1]}" + ) + ind += 1 + if not delete_no_time: + # 先找到wrongtime元素的原始位置,再从原列表删除 + remaining_after_first = [ + i for i in range(len(listdata)) if i not in no_time + ] + second_deleted_original = [ + remaining_after_first[t] for t in wrong_time + ] + data_with_time_ok = [ + listdata[j] + for j in range(len(listdata)) + if j not in second_deleted_original + ] + wrong_time = second_deleted_original + else: + data_with_time_ok = [ + data_with_time[j] + for j in range(len(data_with_time)) + if j not in wrong_time + ] + return data_with_time_ok, no_time, wrong_time + + +def savecsv(listdata, outputfile): + # 处理数字转换 + processed = [] + for row in listdata: + new_row = [] + for item in row: + if isinstance(item, (int, float)): + new_row.append(int(item)) + else: + new_row.append(item) + processed.append(new_row) + + # 写入CSV文件 + with open(outputfile, "w", newline="") as f: + csv.writer(f).writerows(processed) + print(f"已保存至{outputfile}") + + +def find_csv_files(root_dir): + csv_files = [] + for root, dirs, files in os.walk(root_dir): + for file in files: + if file.lower().endswith(".csv"): + csv_path = os.path.join(root, file) + csv_files.append(csv_path) + return csv_files + + +def easydata2data(easydata): + # 测试用函数 + # easydata格式:[[[序号,数量][序号,数量][序号,数量]],[[序号,数量][序号,数量][序号,数量]],结果],没有的序号和数量留-1,序号是真实序号 + datalist = [0] * MONSTER_NUM * 2 + for i in easydata[0]: + if i[0] > 0: + datalist[i[0] - 1] = i[1] + for i in easydata[1]: + if i[0] > 0: + datalist[i[0] - 1 + MONSTER_NUM] = i[1] + datalist.extend(easydata[2]) + return datalist + + +def find_where_from(easydata, floder_path): + # easydata格式:[[[序号,数量][序号,数量][序号,数量]],[[序号,数量][序号,数量][序号,数量]],结果],没有的序号和数量留-1,序号是真实序号 + datalist = [0] * MONSTER_NUM * 2 + from_list = [] + for i in easydata[0]: + if i[0] > 0: + datalist[i[0] - 1] = i[1] + for i in easydata[1]: + if i[0] > 0: + datalist[i[0] - 1 + MONSTER_NUM] = i[1] + datalist.extend(easydata[2]) + print(datalist) + csvlist = find_csv_files(floder_path) + for c in csvlist: + print(c) + for c in csvlist: + print(f"正在检查:{c}…………………………") + lines_num = MONSTER_NUM * 2 + datafull = [] + row_id = 0 + with open(c, "r") as file: + csv_reader = csv.reader(file) + for row in csv_reader: + if ( + isfloat(row[0]) + and "" not in row[:lines_num] + and len(row) > lines_num + ): + numbers = list( + map(int, map(float, row[:lines_num])) + ) # 转换为整数列表 + vals = [row[lines_num]] + if datalist == numbers + vals: + from_list.append([c, row_id + 1]) + print(f"数据来源于:{c},第{row_id+1}行!") + row_id += 1 + str_from = "" + if from_list != []: + str_from = "\n".join( + [i[0] + "第:" + str(i[1]) + "行" for i in from_list] + ) + print(f"可能的数据来源:{str_from}") + + +def is_distance_not_over_60(a, b, c, d): + # a, b = interval1 + # c, d = interval2 + # 判断区间是否有交集 + if max(a, c) <= min(b, d): + return True # 有交集时距离为0,未超过60 + # 计算不重叠时的间隔 + if b < c: + distance = c - b # interval1在左,interval2在右 + else: + distance = a - d # interval2在左,interval1在右 + return distance <= 60 + + +def is_list_true_np(fulllist): + cost_list = [ + [2, 0], + [2, 0.1], + [7, 0], + [7, 0], + [3, 0.1], + [10, 0], + [25, 15], + [22, 0], + [25, 100], + [7, 0], + [5, 0.2], + [7, 0], + [2, 0], + [15, 2], + [13, 0], + [12, 1.5], + [6, 0.2], + [18, 3], + [15, 3], + [18, 1], + [11, 0], + [10, 1], + [16, 0], + [5, 0.5], + [15, 2], + [14, 0], + [30, 0], + [35, 100], + [-1, -1], + [-1, -1], + [-1, -1], + [6, 0.5], + [6, 0], + [16, 0], + [15, 5], + [11, 0], + [26, 0], + [15, 0], + [4, 0.1], + [10, 0], + [21, 0], + [5, 0.2], + [18, 0], + [9, 1.5], + [8, 0.5], + [16, 0], + [21, 0], + [7, 0], + [36, 10], + [10, 2], + [30, 15], + [25, 0], + [27, 0], + [32, 6], + [25, 50], + [15, 5], + ] + round_cost_list = [ + [50, 70], + [70, 90], + [90, 110], + [110, 130], + [120, 160], + [140, 180], + [160, 200], + [170, 230], + [190, 250], + [210, 270], + ] + + # Convert to numpy arrays + cost_arr = np.array(cost_list) + round_cost_arr = np.array(round_cost_list) + round_low = round_cost_arr[:, 0] + round_high = round_cost_arr[:, 1] + + # Validity mask for cost entries not equal to [-1, -1] + valid_mask = np.all(cost_arr != [-1, -1], axis=1) + + # Split the input into left and right parts + fulllist_np = np.array([i[:112] for i in fulllist], dtype=np.float64) + N = fulllist_np.shape[0] + left_part = fulllist_np[:, :56] + right_part = fulllist_np[:, 56:112] + + # Calculate valid entries (cost not [-1,-1] and count >0) + valid_left = valid_mask[np.newaxis, :] & (left_part > 0) + valid_right = valid_mask[np.newaxis, :] & (right_part > 0) + + # Compute mincostL and maxcostL for left + left_min_terms = ( + (left_part - 1) * cost_arr[np.newaxis, :, 0] + + ((left_part - 1) * (left_part - 2) * cost_arr[np.newaxis, :, 1]) / 2 + + 0.01 + ) * valid_left + mincostL = left_min_terms.sum(axis=1) + + left_max_terms = ( + (left_part + 1) * cost_arr[np.newaxis, :, 0] + + (left_part * (left_part + 1) * cost_arr[np.newaxis, :, 1]) / 2 + - 0.01 + ) * valid_left + maxcostL = left_max_terms.sum(axis=1) + + # Compute mincostR and maxcostR for right + right_min_terms = ( + (right_part - 1) * cost_arr[np.newaxis, :, 0] + + ((right_part - 1) * (right_part - 2) * cost_arr[np.newaxis, :, 1]) + / 2 + + 0.01 + ) * valid_right + mincostR = right_min_terms.sum(axis=1) + + right_max_terms = ( + (right_part + 1) * cost_arr[np.newaxis, :, 0] + + (right_part * (right_part + 1) * cost_arr[np.newaxis, :, 1]) / 2 + - 0.01 + ) * valid_right + maxcostR = right_max_terms.sum(axis=1) + + # Check overlap with round costs + left_low = np.maximum(mincostL[:, np.newaxis], round_low) + left_high = np.minimum(maxcostL[:, np.newaxis], round_high) + left_cond = left_low <= left_high + + right_low = np.maximum(mincostR[:, np.newaxis], round_low) + right_high = np.minimum(maxcostR[:, np.newaxis], round_high) + right_cond = right_low <= right_high + + both_cond = left_cond & right_cond + any_round = np.any(both_cond, axis=1) + + # Get indices where no round condition is satisfied + false_indices = np.where(~any_round)[0].tolist() + return false_indices + + +def is_list_true(onelist): + roundlist = [] + # 费用和附加费用,写死在代码里吧,不想读文件了。 + cost_list = [ + [2, 0], + [2, 0.1], + [7, 0], + [7, 0], + [3, 0.1], + [10, 0], + [25, 15], + [22, 0], + [25, 100], + [7, 0], + [5, 0.2], + [7, 0], + [2, 0], + [15, 2], + [13, 0], + [12, 1.5], + [6, 0.2], + [18, 3], + [15, 3], + [18, 1], + [11, 0], + [10, 1], + [16, 0], + [5, 0.5], + [15, 2], + [14, 0], + [30, 0], + [35, 100], + [-1, -1], + [-1, -1], + [-1, -1], + [6, 0.5], + [6, 0], + [16, 0], + [15, 5], + [11, 0], + [26, 0], + [15, 0], + [4, 0.1], + [10, 0], + [21, 0], + [5, 0.2], + [18, 0], + [9, 1.5], + [8, 0.5], + [16, 0], + [21, 0], + [7, 0], + [36, 10], + [10, 2], + [30, 15], + [25, 0], + [27, 0], + [32, 6], + [25, 50], + [15, 5], + ] + # + round_cost_list = [ + [50, 70], + [70, 90], + [90, 110], + [110, 130], + [120, 160], + [140, 180], + [160, 200], + [170, 230], + [190, 250], + [210, 270], + ] + left = onelist[:MONSTER_NUM] + right = onelist[MONSTER_NUM : MONSTER_NUM * 2] + # result = onelist[MONSTER_NUM*2] + # print(left,right,result) + mincostL = sum( + [ + (left[i] - 1) * cost_list[i][0] + + (left[i] - 1) * (left[i] - 2) * cost_list[i][1] / 2 + + 0.01 + for i in range(len(left)) + if (cost_list[i] != [-1, -1]) and (left[i] > 0) + ] + ) + maxcostL = sum( + [ + (left[i] + 1) * cost_list[i][0] + + left[i] * (left[i] + 1) * cost_list[i][1] / 2 + - 0.01 + for i in range(len(left)) + if (cost_list[i] != [-1, -1]) and (left[i] > 0) + ] + ) + mincostR = sum( + [ + (right[i] - 1) * cost_list[i][0] + + (right[i] - 1) * (right[i] - 2) * cost_list[i][1] / 2 + + 0.01 + for i in range(len(right)) + if (cost_list[i] != [-1, -1]) and (right[i] > 0) + ] + ) + maxcostR = sum( + [ + (right[i] + 1) * cost_list[i][0] + + right[i] * (right[i] + 1) * cost_list[i][1] / 2 + - 0.01 + for i in range(len(right)) + if (cost_list[i] != [-1, -1]) and (right[i] > 0) + ] + ) + print(mincostL, maxcostL, mincostR, maxcostR) + for i in range(len(round_cost_list)): + if max(mincostL, round_cost_list[i][0]) <= min( + maxcostL, round_cost_list[i][1] + ) and max(mincostR, round_cost_list[i][0]) <= min( + maxcostR, round_cost_list[i][1] + ): + roundlist.append(i) + if roundlist != []: + return True + else: + print(f"{onelist}is not true!!!") + return False + # return is_distance_not_over_60(mincostL,maxcostL,mincostR,maxcostR) + + +def recognize_review( + data, img_floder, matched_threshold=0.1, ocr_threshold=0.5 +): + print("正在进行识别数据检查") + print("data行数:", len(data)) + ref_row = [0] * (recognize.MONSTER_COUNT * 2) + need_delete = [False] * len(data) + for idx, row in tqdm.tqdm( + enumerate(data), total=len(data), desc="Processing rows" + ): + ref_row = [0] * (recognize.MONSTER_COUNT * 2) + try: + img_name = row[recognize.MONSTER_COUNT * 2 + 1] + img_path = img_floder / Path(img_name) + if not img_path.exists(): + print(f"未找到对应的图像: {img_name} ") + continue + img = cv2.imread(img_path) + main_roi = ((0, 0), (img.shape[1], img.shape[0])) + results = recognize.process_regions( + main_roi, img, matched_threshold, ocr_threshold + ) + # 处理结果 + for res in results: + if "error" in res: + print( + f"识别失败 行号: {idx}, 图片: {img_name}, 错误类型: {res['error']}", + file=sys.stderr, + ) + break + if res["matched_id"]: + if res["region_id"] < 3: + ref_row[res["matched_id"] - 1] = int(res["number"]) + else: + ref_row[res["matched_id"] - 1 + MONSTER_NUM] = int( + res["number"] + ) + else: + # 检查数据行是否与参考行匹配 + data_row = row[0 : recognize.MONSTER_COUNT * 2] + if data_row != ref_row: + print( + f"找到不匹配的数据行: {idx} 行,对应图片文件: {img_name}", + file=sys.stderr, + ) + print(f"识别结果 : {ref_row}", file=sys.stderr) + print(f"文件数据 : {data_row}", file=sys.stderr) + need_delete[idx] = True + else: + need_delete[idx] = False + except Exception as e: + logging.exception(f"Error processing line {idx}", e) + need_delete[idx] = True + newdata = [row for idx, row in enumerate(data) if not need_delete[idx]] + deleted = [idx for idx, del_flag in enumerate(need_delete) if del_flag] + return newdata, deleted + + +# newdata,deleted,ori_len = read_and_remove_zeros('0502.csv',MONSTER_NUM=56) +# _,inc = remove_duplicate_subsequences() +# print('数据例:',newdata[:3]) +# result,deleted2 = do_duplicate(newdata) +# print(deleted,deleted2) +# dt = ori_pos(ori_len,deleted,deleted2) +# print(dt) +# print('筛选后数据总长度:',len(result)) +# view_monster_counts(newdata) +# del_duplicate_by_time(newdata) + + +def process_full( + filename, + do_remove_duplicate_subsequences=False, + delete_no_time=True, + open_black_list=True, + re_recognize_imgs=False, + img_floder="", + matched_threshold=0.1, + ocr_threshold=0.5, +): + wrong_type_list = [] + newdata, deleted0, ori_len = read_and_remove_zeros( + filename, MONSTER_NUM=56 + ) + deleted1 = [] + if do_remove_duplicate_subsequences: + newdata, deleted1 = do_duplicate(newdata) + newdata, deleted2, deleted3 = del_duplicate_by_time( + newdata, delete_no_time + ) + if not delete_no_time: + deleted2 = [] + black_listed, deleted4, deleted5, newdata = view_monster_counts(newdata) + deleted6 = [] + if re_recognize_imgs: + newdata, deleted6 = recognize_review( + newdata, img_floder, matched_threshold, ocr_threshold + ) + deleted7 = [] + if open_black_list: + newdata, deleted7 = process_black_list(newdata) + + dl = deleted0 + flag = 0 + for i in [ + deleted1, + deleted2, + deleted3, + deleted4, + deleted5, + deleted6, + deleted7, + ]: + if i != []: + dl, secori = ori_pos(ori_len, dl, i) + if flag == 0: + wrong_type_list.append( + ["不合法的数据:", merge([i + 1 for i in deleted0])] + ) + wrong_type_list.append( + ["重复出现的连续数据*:", merge([i + 1 for i in secori])] + ) + elif flag == 1: + wrong_type_list.append( + ["未包含时间轴的数据:", merge([i + 1 for i in secori])] + ) + elif flag == 2: + wrong_type_list.append( + ["时间轴信息重复的数据:", merge([i + 1 for i in secori])] + ) + elif flag == 3: + wrong_type_list.append( + ["怪物信息错误的数据:", merge([i + 1 for i in secori])] + ) + elif flag == 4: + wrong_type_list.append( + [ + "不符合出怪权重规则的数据:", + merge([i + 1 for i in secori]), + ] + ) + elif flag == 5: + wrong_type_list.append( + ["经图片识别错误的数据*:", merge([i + 1 for i in secori])] + ) + elif flag == 6: + wrong_type_list.append( + ["黑名单内数据:", merge([i + 1 for i in secori])] + ) + flag += 1 + return black_listed, newdata, dl, wrong_type_list + + +def test1(): + black_listed, newdata, dl, wrong_type_list = process_full( + "0502processed.csv" + ) + dllist = [i + 1 for i in dl] + print(f"删除了{dllist}行的数据") + for i in wrong_type_list: + print(i) + savecsv(newdata, "0502processed2.csv") + + +def process_floder( + flodername, + savefilename, + lastsavefilename, + do_remove_duplicate_subsequences=True, + delete_no_time=True, + open_black_list=True, + re_recognize_imgs=False, + img_floder="", + matched_threshold=0.1, + ocr_threshold=0.5, +): + """ + 输入: + flodername:需要处理的文件夹名 + savefilename:全部整合保存到的文件名(不进行总去重) + lastsavefilename:全部整合并去重保存到的最终文件名 + do_remove_duplicate_subsequences:是否清理连续3个以上重复元素的重复序列 + delete_no_time:是否删除没有时间戳的数据行 + """ + global black_list_rows + full_data_list = [] + csvlist = find_csv_files(flodername) + for csv in csvlist: + print(csv) + for csv in csvlist: + print(f"正在处理:{csv}…………………………") + black_listed, newdata, dl, wrong_type_list = process_full( + csv, + do_remove_duplicate_subsequences, + delete_no_time, + open_black_list, + re_recognize_imgs, + img_floder, + matched_threshold, + ocr_threshold, + ) + dllist = [i + 1 for i in dl] + print(f"删除了{merge(dllist)}行的数据") + for i in wrong_type_list: + print(i) + if not black_listed: + # 未进黑名单则合并至全部数据 + full_data_list += newdata + else: + print(f"该数据为30人局数据,自动进入黑名单,不计入总数据!") + if len(newdata) < 5000: # 不是整合数据 + black_list_rows += newdata + savecsv(full_data_list, savefilename) + black_listed, newdata, dl, wrong_type_list = process_full( + savefilename, + do_remove_duplicate_subsequences, + delete_no_time, + open_black_list, + re_recognize_imgs, + img_floder, + matched_threshold, + ocr_threshold, + ) + # 保存后再总处理去重 + dllist = [i + 1 for i in dl] + print(f"删除了{merge(dllist)}行的数据") + for i in wrong_type_list: + print(i) + savecsv(newdata, lastsavefilename) + + +def process_black_list(full_data): + # 黑名单里所有的数据检测到重复的就删 + global black_list_rows + delete_rows = [] + ok_data = [] + idx = 0 + for i in full_data: + if i in black_list_rows: + delete_rows.append(idx) + else: + ok_data.append(i) + idx += 1 + print(f"黑名单内数据:{merge(delete_rows)}") + return ok_data, delete_rows + + +def process_file( + filename, + savefilename, + do_remove_duplicate_subsequences=True, + delete_no_time=True, + open_black_list=True, + re_recognize_imgs=False, + img_floder="", + matched_threshold=0.1, + ocr_threshold=0.5, +): + """ + 输入: + filename:需要处理的文件名 + savefilename:处理后保存到的文件名 + do_remove_duplicate_subsequences:是否清理连续3个以上重复元素的重复序列 + delete_no_time:是否删除没有时间戳的数据行 + """ + black_listed, newdata, dl, wrong_type_list = process_full( + filename, + do_remove_duplicate_subsequences, + delete_no_time, + open_black_list, + re_recognize_imgs, + img_floder, + matched_threshold, + ocr_threshold, + ) + # 保存后再总处理去重 + dllist = [i + 1 for i in dl] + print(f"删除了{merge(dllist)}行的数据") + for i in wrong_type_list: + print(i) + savecsv(newdata, savefilename) + + +# process_floder(r'D:\Backup\Downloads\arcdata','arcdata_fullaa.csv','arcdata_full_washed_plus.csv') + +import tkinter as tk +from tkinter import ttk, filedialog, messagebox +import sys +import threading +import queue + + +class RedirectText(object): + def __init__(self, text_widget, log_file="processing.log"): + self.text_widget = text_widget + self.log_file = log_file + self.queue = queue.Queue() + self.root = text_widget.master + self.lock = threading.Lock() + + # 初始化日志文件 + self.setup_logfile() + + def setup_logfile(self): + try: + # 使用追加模式打开日志文件 + self.log_fd = open(self.log_file, "a", encoding="utf-8") + except Exception as e: + self.log_fd = None + self.write(f"无法打开日志文件: {str(e)}\n") + + def write(self, message): + # 写入日志文件(带线程锁) + with self.lock: + if self.log_fd: + try: + self.log_fd.write(message) + self.log_fd.flush() # 确保立即写入磁盘 + except Exception as e: + self.log_fd = None + self.queue.put(f"日志写入失败: {str(e)}\n") + + # 写入队列供界面显示 + self.queue.put(message) + self.root.after(100, self.update_text) + + def update_text(self): + while not self.queue.empty(): + msg = self.queue.get_nowait() + self.text_widget.insert(tk.END, msg) + self.text_widget.see(tk.END) + + def flush(self): + pass + + def close_logfile(self): + with self.lock: + if self.log_fd: + self.log_fd.close() + self.log_fd = None + + +class ProcessingThread(threading.Thread): + def __init__(self, func, args=(), kwargs={}, callback=None): + super().__init__() + self.func = func + self.args = args + self.kwargs = kwargs + self.callback = callback + self.daemon = True + self.exception = None + + def run(self): + try: + self.func(*self.args, **self.kwargs) + except Exception as e: + self.exception = e + finally: + if self.callback: + self.callback(self.exception) + + +def create_gui(): + root = tk.Tk() + root.title("数据搅拌机") + root.geometry("800x600") + + # 在此处定义关闭事件处理函数(推荐位置) + def on_close(): + sys.stdout.close_logfile() # 关闭日志文件 + if messagebox.askokcancel( + "退出", "确定要退出程序吗?" + ): # 添加确认对话框 + root.destroy() # 销毁窗口 + + # 绑定关闭事件处理 + root.protocol("WM_DELETE_WINDOW", on_close) + + # 创建文本输出区域 + output_text = tk.Text(root, wrap=tk.WORD) + output_text.grid( + row=3, column=0, columnspan=2, padx=10, pady=10, sticky="nsew" + ) + sys.stdout = RedirectText(output_text, "data_processing.log") + + # 处理文件夹的Frame + folder_frame = ttk.LabelFrame( + root, + text="处理文件夹(处理文件夹及其所有子文件夹下的CSV文件,并合并为一个)", + ) + folder_frame.grid(row=0, column=0, padx=10, pady=5, sticky="ew") + + # 处理文件夹的组件 + ttk.Label(folder_frame, text="选择文件夹:").grid( + row=0, column=0, padx=5, sticky="w" + ) + folder_path = tk.StringVar() + folder_entry = ttk.Entry(folder_frame, textvariable=folder_path, width=40) + folder_entry.grid(row=0, column=1, padx=5) + ttk.Button( + folder_frame, + text="浏览", + command=lambda: folder_path.set(filedialog.askdirectory()), + ).grid(row=0, column=2, padx=5) + + ttk.Label(folder_frame, text="中间保存文件(不进行最终去重):").grid( + row=1, column=0, padx=5, sticky="w" + ) + interim_save = tk.StringVar() + ttk.Entry(folder_frame, textvariable=interim_save, width=40).grid( + row=1, column=1, padx=5 + ) + ttk.Button( + folder_frame, + text="浏览", + command=lambda: interim_save.set( + filedialog.asksaveasfilename( + defaultextension=".csv", + filetypes=[("CSV文件", "*.csv"), ("所有文件", "*.*")], + ) + ), + ).grid(row=1, column=2, padx=5) + + ttk.Label(folder_frame, text="最终保存文件:").grid( + row=2, column=0, padx=5, sticky="w" + ) + final_save = tk.StringVar() + ttk.Entry(folder_frame, textvariable=final_save, width=40).grid( + row=2, column=1, padx=5 + ) + ttk.Button( + folder_frame, + text="浏览", + command=lambda: final_save.set( + filedialog.asksaveasfilename( + defaultextension=".csv", + filetypes=[("CSV文件", "*.csv"), ("所有文件", "*.*")], + ) + ), + ).grid(row=2, column=2, padx=5) + + # 复选框 + remove_dup = tk.BooleanVar(value=False) + ttk.Checkbutton( + folder_frame, + text="不依赖时间戳清理重复子序列(在大数据集会非常慢,通常关闭)", + variable=remove_dup, + ).grid(row=3, column=0, columnspan=3, sticky="w") + + del_time = tk.BooleanVar(value=True) + ttk.Checkbutton( + folder_frame, text="删除无时间戳数据", variable=del_time + ).grid(row=4, column=0, columnspan=3, sticky="w") + + open_black = tk.BooleanVar(value=True) + ttk.Checkbutton( + folder_frame, + text="将黑名单文件内的所有数据行同时加入黑名单", + variable=open_black, + ).grid(row=5, column=0, columnspan=3, sticky="w") + + # 修改后的图片识别行(将复选框和阈值输入放在同一行) + re_recognize_var = tk.BooleanVar(value=False) + ttk.Checkbutton( + folder_frame, + text="启用图片二次识别(必须指定图片路径)", + variable=re_recognize_var, + ).grid(row=6, column=0, padx=5, sticky="w") + + # 添加匹配阈值设置 + ttk.Label(folder_frame, text="匹配阈值:").grid( + row=6, column=1, padx=(20, 5), sticky="e" + ) + matched_threshold_var = tk.DoubleVar(value=0.1) + ttk.Entry(folder_frame, textvariable=matched_threshold_var, width=6).grid( + row=6, column=2, sticky="w" + ) + + # 添加OCR阈值设置 + ttk.Label(folder_frame, text="OCR阈值:").grid( + row=6, column=3, padx=(20, 5), sticky="e" + ) + ocr_threshold_var = tk.DoubleVar(value=0.5) + ttk.Entry(folder_frame, textvariable=ocr_threshold_var, width=6).grid( + row=6, column=4, sticky="w" + ) + + # 调整后续行号(原row=6改为row=7开始) + ttk.Label(folder_frame, text="图片文件夹路径:").grid( + row=7, column=0, padx=5, sticky="w" + ) + img_folder_path = tk.StringVar() + ttk.Entry(folder_frame, textvariable=img_folder_path, width=40).grid( + row=7, column=1, padx=5 + ) + ttk.Button( + folder_frame, + text="浏览", + command=lambda: img_folder_path.set(filedialog.askdirectory()), + ).grid(row=7, column=2, padx=5) + + # 调整处理文件夹按钮的行号 + + # 处理文件夹按钮 + folder_button = ttk.Button(folder_frame, text="执行处理") + folder_button.grid(row=8, column=0, columnspan=5, pady=5) + + # 处理文件的Frame + file_frame = ttk.LabelFrame(root, text="处理单个文件") + file_frame.grid(row=1, column=0, padx=10, pady=5, sticky="ew") + + # 处理文件的组件 + ttk.Label(file_frame, text="选择文件:").grid( + row=0, column=0, padx=5, sticky="w" + ) + file_path = tk.StringVar() + ttk.Entry(file_frame, textvariable=file_path, width=40).grid( + row=0, column=1, padx=5 + ) + ttk.Button( + file_frame, + text="浏览", + command=lambda: file_path.set(filedialog.askopenfilename()), + ).grid(row=0, column=2, padx=5) + + ttk.Label(file_frame, text="保存路径:").grid( + row=1, column=0, padx=5, sticky="w" + ) + save_path = tk.StringVar() + ttk.Entry(file_frame, textvariable=save_path, width=40).grid( + row=1, column=1, padx=5 + ) + ttk.Button( + file_frame, + text="浏览", + command=lambda: save_path.set( + filedialog.asksaveasfilename( + defaultextension=".csv", + filetypes=[("CSV文件", "*.csv"), ("所有文件", "*.*")], + ) + ), + ).grid(row=1, column=2, padx=5) + + # 复选框 + remove_dup_file = tk.BooleanVar(value=False) + ttk.Checkbutton( + file_frame, + text="不依赖时间戳清理重复子序列(在大数据集会非常慢,通常关闭)", + variable=remove_dup_file, + ).grid(row=2, column=0, columnspan=3, sticky="w") + + del_time_file = tk.BooleanVar(value=True) + ttk.Checkbutton( + file_frame, text="删除无时间戳数据", variable=del_time_file + ).grid(row=3, column=0, columnspan=3, sticky="w") + + open_black_file = tk.BooleanVar(value=True) + ttk.Checkbutton( + file_frame, + text="将黑名单文件内的所有数据行同时加入黑名单", + variable=open_black_file, + ).grid(row=4, column=0, columnspan=3, sticky="w") + + # 修改后的图片识别行 + re_recognize_file_var = tk.BooleanVar(value=False) + ttk.Checkbutton( + file_frame, + text="启用图片二次识别(必须指定图片路径)", + variable=re_recognize_file_var, + ).grid(row=5, column=0, padx=5, sticky="w") + + # 匹配阈值 + ttk.Label(file_frame, text="匹配阈值:").grid( + row=5, column=1, padx=(20, 5), sticky="e" + ) + matched_threshold_file_var = tk.DoubleVar(value=0.1) + ttk.Entry( + file_frame, textvariable=matched_threshold_file_var, width=6 + ).grid(row=5, column=2, sticky="w") + + # OCR阈值 + ttk.Label(file_frame, text="OCR阈值:").grid( + row=5, column=3, padx=(20, 5), sticky="e" + ) + ocr_threshold_file_var = tk.DoubleVar(value=0.5) + ttk.Entry(file_frame, textvariable=ocr_threshold_file_var, width=6).grid( + row=5, column=4, sticky="w" + ) + + # 调整后续行号 + ttk.Label(file_frame, text="图片文件夹路径:").grid( + row=6, column=0, padx=5, sticky="w" + ) + img_folder_file_path = tk.StringVar() + ttk.Entry(file_frame, textvariable=img_folder_file_path, width=40).grid( + row=6, column=1, padx=5 + ) + ttk.Button( + file_frame, + text="浏览", + command=lambda: img_folder_file_path.set(filedialog.askdirectory()), + ).grid(row=6, column=2, padx=5) + + # 调整处理文件按钮的行号 + + # 处理文件按钮 + file_button = ttk.Button(file_frame, text="执行处理") + file_button.grid(row=7, column=0, columnspan=5, pady=5) + + # 配置网格权重 + root.grid_rowconfigure(3, weight=1) + root.grid_columnconfigure(0, weight=1) + + # 按钮回调函数 + def process_folder_wrapper(): + folder = folder_path.get() + interim = interim_save.get() + final = final_save.get() + if not folder or not interim or not final: + messagebox.showerror("错误", "请填写所有路径") + return + + folder_button.config(state=tk.DISABLED) + + def callback(e): + folder_button.config(state=tk.NORMAL) + if e: + messagebox.showerror("错误", str(e)) + else: + messagebox.showinfo("完成", "文件夹处理完成") + + thread = ProcessingThread( + func=process_floder, + args=( + folder, + interim, + final, + remove_dup.get(), + del_time.get(), + open_black.get(), + re_recognize_var.get(), + Path(img_folder_path.get()), + matched_threshold_var.get(), + ocr_threshold_var.get(), + ), + callback=callback, + ) + thread.start() + + def process_file_wrapper(): + input_file = file_path.get() + output_file = save_path.get() + if not input_file or not output_file: + messagebox.showerror("错误", "请填写所有路径") + return + + file_button.config(state=tk.DISABLED) + + def callback(e): + file_button.config(state=tk.NORMAL) + if e: + messagebox.showerror("错误", str(e)) + else: + messagebox.showinfo("完成", "文件处理完成") + + thread = ProcessingThread( + func=process_file, + args=( + input_file, + output_file, + remove_dup_file.get(), + del_time_file.get(), + open_black_file.get(), + re_recognize_file_var.get(), + Path(img_folder_file_path.get()), + matched_threshold_file_var.get(), + ocr_threshold_file_var.get(), + ), + callback=callback, + ) + thread.start() + + # 绑定按钮命令 + folder_button.config(command=process_folder_wrapper) + file_button.config(command=process_file_wrapper) + + return root + + +if __name__ == "__main__": + # 请确保以下函数已经正确导入或定义: + # process_floder, process_file, find_csv_files, savecsv + + app = create_gui() + app.mainloop() diff --git "a/data_train/\346\225\260\346\215\256\345\220\210\345\271\266.py" b/src/tools/merge_data.py similarity index 69% rename from "data_train/\346\225\260\346\215\256\345\220\210\345\271\266.py" rename to src/tools/merge_data.py index 4c31aa0..879b10b 100644 --- "a/data_train/\346\225\260\346\215\256\345\220\210\345\271\266.py" +++ b/src/tools/merge_data.py @@ -14,12 +14,16 @@ if str(project_root) not in sys.path: sys.path.insert(0, str(project_root)) -#from config import MONSTER_COUNT, FIELD_FEATURE_COUNT config会读取图片导致非常慢 +# from config import MONSTER_COUNT, FIELD_FEATURE_COUNT config会读取图片导致非常慢 + def load_monster_data(): - monster_data = pd.read_csv('monster_greenvine.csv', index_col="id", encoding='utf-8-sig') + monster_data = pd.read_csv( + "monster_greenvine.csv", index_col="id", encoding="utf-8-sig" + ) return monster_data + MONSTER_DATA = load_monster_data() # 全局变量 @@ -31,9 +35,15 @@ def get_expected_header(): """根据配置生成预期表头""" if FIELD_FEATURE_COUNT > 0: header = [f"{i + 1}L" for i in range(MONSTER_COUNT)] - header += [f"{i + 1}LF" for i in range(MONSTER_COUNT, MONSTER_COUNT + FIELD_FEATURE_COUNT)] + header += [ + f"{i + 1}LF" + for i in range(MONSTER_COUNT, MONSTER_COUNT + FIELD_FEATURE_COUNT) + ] header += [f"{i + 1}R" for i in range(MONSTER_COUNT)] - header += [f"{i + 1}RF" for i in range(MONSTER_COUNT, MONSTER_COUNT + FIELD_FEATURE_COUNT)] + header += [ + f"{i + 1}RF" + for i in range(MONSTER_COUNT, MONSTER_COUNT + FIELD_FEATURE_COUNT) + ] header += ["Result", "ImgPath"] else: header = [f"{i + 1}L" for i in range(MONSTER_COUNT)] @@ -44,12 +54,12 @@ def get_expected_header(): def read_csv_from_zip(zip_ref, csv_filename): """从 ZIP 文件流中直接读取 CSV 数据,尝试多种编码""" - encodings = ['utf-8-sig', 'utf-8', 'gbk', 'gb18030', 'big5', 'latin1'] + encodings = ["utf-8-sig", "utf-8", "gbk", "gb18030", "big5", "latin1"] for encoding in encodings: try: with zip_ref.open(csv_filename) as f: - text_f = io.TextIOWrapper(f, encoding=encoding, newline='') + text_f = io.TextIOWrapper(f, encoding=encoding, newline="") reader = csv.reader(text_f) try: header = next(reader) @@ -65,8 +75,8 @@ def read_csv_from_zip(zip_ref, csv_filename): def process_archives(merge_images=True, extract_result_images=False): package_dir = base_dir / "package" - target_images_dir = base_dir / 'images' - target_csv_path = base_dir / 'arknights.csv' + target_images_dir = base_dir / "images" + target_csv_path = base_dir / "arknights.csv" # 1. 目录准备 (移除旧的清空逻辑,保证增量更新) if not package_dir.exists(): @@ -82,12 +92,16 @@ def process_archives(merge_images=True, extract_result_images=False): # 2. 构建现有 CSV 数据的索引集合 seen_csv_img_paths = set() - is_csv_initialized = False # 用于判断是否需要写入表头 + is_csv_initialized = False # 用于判断是否需要写入表头 if target_csv_path.exists(): - print(f"检测到已存在的 {target_csv_path.name},正在读取历史记录构建索引...") + print( + f"检测到已存在的 {target_csv_path.name},正在读取历史记录构建索引..." + ) try: - with open(target_csv_path, 'r', encoding='utf-8-sig', newline='') as f: + with open( + target_csv_path, "r", encoding="utf-8-sig", newline="" + ) as f: reader = csv.reader(f) try: header = next(reader) @@ -96,10 +110,14 @@ def process_archives(merge_images=True, extract_result_images=False): for row in reader: if len(row) > img_path_idx: seen_csv_img_paths.add(row[img_path_idx]) - print(f"-> 成功加载 {len(seen_csv_img_paths)} 条历史记录的索引。") + print( + f"-> 成功加载 {len(seen_csv_img_paths)} 条历史记录的索引。" + ) else: - print("-> 警告:已存在 CSV 的表头与配置不符,将创建新文件或覆写。") - target_csv_path.unlink() # 表头不对则删除重建 + print( + "-> 警告:已存在 CSV 的表头与配置不符,将创建新文件或覆写。" + ) + target_csv_path.unlink() # 表头不对则删除重建 except StopIteration: # 文件为空 pass @@ -108,7 +126,7 @@ def process_archives(merge_images=True, extract_result_images=False): target_csv_path.unlink(missing_ok=True) # 定义可能的文件后缀,涵盖常见大小写 - possible_extensions = ['.jpg', '.png', '.jpeg', '.JPG', '.PNG', '.JPEG'] + possible_extensions = [".jpg", ".png", ".jpeg", ".JPG", ".PNG", ".JPEG"] zip_files = list(package_dir.glob("*.zip")) print(f"\n找到 {len(zip_files)} 个压缩包,准备进行增量处理...") @@ -121,17 +139,19 @@ def process_archives(merge_images=True, extract_result_images=False): print(f"\n正在处理压缩包: {zip_path.name}") try: - with zipfile.ZipFile(zip_path, 'r') as zf: + with zipfile.ZipFile(zip_path, "r") as zf: # 建立 namelist 的 O(1) 查找集合 zip_namelist_set = set(zf.namelist()) - + # ========================================== # 任务 1:完全独立处理 CSV 文件 # ========================================== - csv_members = [m for m in zip_namelist_set if m.endswith('arknights.csv')] + csv_members = [ + m for m in zip_namelist_set if m.endswith("arknights.csv") + ] zip_added_csv_count = 0 - + for csv_member in csv_members: header, data, encoding = read_csv_from_zip(zf, csv_member) @@ -153,17 +173,24 @@ def process_archives(merge_images=True, extract_result_images=False): # 追加写入 CSV if zip_new_csv_rows: - mode = 'a' if is_csv_initialized else 'w' - with open(target_csv_path, mode, newline='', encoding='utf-8-sig') as f: + mode = "a" if is_csv_initialized else "w" + with open( + target_csv_path, + mode, + newline="", + encoding="utf-8-sig", + ) as f: writer = csv.writer(f) if not is_csv_initialized: writer.writerow(expected_header) is_csv_initialized = True writer.writerows(zip_new_csv_rows) - + zip_added_csv_count += len(zip_new_csv_rows) print(f" -> {csv_member} (编码: {encoding})") - print(f" CSV: 新增 {len(zip_new_csv_rows)} 条,重复跳过 {skip_csv_count} 条") + print( + f" CSV: 新增 {len(zip_new_csv_rows)} 条,重复跳过 {skip_csv_count} 条" + ) total_added_rows += zip_added_csv_count @@ -180,29 +207,39 @@ def process_archives(merge_images=True, extract_result_images=False): # 排除 Mac 系统压缩可能产生的隐藏文件干扰 if "__MACOSX" in member: continue - + filename = Path(member).name - is_result_img = filename.rsplit('.', 1)[0].endswith('_result') + is_result_img = filename.rsplit(".", 1)[ + 0 + ].endswith("_result") # 根据参数决定是否跳过 result 图 if is_result_img and not extract_result_images: continue target_img_path = target_images_dir / filename - + # 增量判断:如果本地没有,则解压提取 if target_img_path.exists(): zip_skip_img_count += 1 else: try: with zf.open(member) as source_file: - with open(target_img_path, 'wb') as target_file: - shutil.copyfileobj(source_file, target_file) + with open( + target_img_path, "wb" + ) as target_file: + shutil.copyfileobj( + source_file, target_file + ) zip_extracted_img_count += 1 except Exception as e: - print(f" [错误] 提取图片 {member} 失败: {e}") - - print(f" IMG: 提取新图 {zip_extracted_img_count} 张,已存在跳过 {zip_skip_img_count} 张") + print( + f" [错误] 提取图片 {member} 失败: {e}" + ) + + print( + f" IMG: 提取新图 {zip_extracted_img_count} 张,已存在跳过 {zip_skip_img_count} 张" + ) total_extracted_imgs += zip_extracted_img_count except zipfile.BadZipFile: @@ -216,7 +253,9 @@ def process_archives(merge_images=True, extract_result_images=False): print(f"总计提取新图片: {total_extracted_imgs} 张") -if __name__ == '__main__': - merge_imgs = False # 设置为 True 则提取阵容图 +if __name__ == "__main__": + merge_imgs = False # 设置为 True 则提取阵容图 extract_res_imgs = False # 设置为 True 则同时提取带有 _result 的结果图 - process_archives(merge_images=merge_imgs, extract_result_images=extract_res_imgs) + process_archives( + merge_images=merge_imgs, extract_result_images=extract_res_imgs + ) diff --git a/tools/package.py b/src/tools/package.py similarity index 93% rename from tools/package.py rename to src/tools/package.py index 1552a14..a6d6fd2 100644 --- a/tools/package.py +++ b/src/tools/package.py @@ -19,8 +19,8 @@ def _configure_utf8_stdio(): CONFIG = { "venv_dir": ".venv", # 虚拟环境目录 "source_script": "main.py", # 主程序文件路径 - "icon_file": r"ico/icon_64x64.ico", # 图标文件路径 - "output_dir": "output", # 输出目录 + "icon_file": r"src/resources/assets/icons/icon_64x64.ico", # 图标文件路径 + "output_dir": "build/dist", # 输出目录 "console": True, "add_data": [ # 需要打包的附加数据 (r".venv/Lib/site-packages/rapidocr/default_models.yaml", "rapidocr"), @@ -32,15 +32,10 @@ def _configure_utf8_stdio(): r"C:\Windows\System32\msvcp140.dll", r"C:\Windows\System32\vcruntime140.dll", r"C:\Windows\System32\vcruntime140_1.dll", - # "arknights.csv", - # "models/best_model_full.onnx", - "images", - "platform-tools", - "ico", + "vendor/bin/platform-tools", "pyproject.toml", - "monster.csv", - "monster_greenvine.csv", - "maafw", + "vendor/bin/maafw", + "src", ], } @@ -95,7 +90,7 @@ def copy_additional_files(): # 确保目标目录存在 dest.mkdir(parents=True, exist_ok=True) # 特殊处理images目录,排除tmp和nums子目录 - if src.name == "images": + if src.name == "images" and "resources" in str(src): def ignore_func(dir, names): """忽略tmp和nums子目录""" diff --git a/src/ui/__init__.py b/src/ui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dark_mode_style_fix.py b/src/ui/dark_mode_style_fix.py similarity index 100% rename from dark_mode_style_fix.py rename to src/ui/dark_mode_style_fix.py diff --git a/input_panel_ui.py b/src/ui/input_panel_ui.py similarity index 80% rename from input_panel_ui.py rename to src/ui/input_panel_ui.py index 602ae10..4a41ede 100644 --- a/input_panel_ui.py +++ b/src/ui/input_panel_ui.py @@ -1,14 +1,28 @@ import json import re from collections import defaultdict -from PyQt6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QFrame -from PyQt6.QtWidgets import QLineEdit, QScrollArea, QGridLayout, QSizePolicy, QGraphicsDropShadowEffect -from PyQt6.QtCore import Qt, QTimer, pyqtSignal, QObject, QThread -from PyQt6.QtGui import QPixmap, QImage, QFont, QIcon, QPainter, QColor +from PyQt6.QtWidgets import ( + QWidget, + QVBoxLayout, + QHBoxLayout, + QLabel, + QPushButton, + QFrame, +) +from PyQt6.QtWidgets import ( + QLineEdit, + QScrollArea, + QGridLayout, + QSizePolicy, + QGraphicsDropShadowEffect, +) +from PyQt6.QtCore import Qt, pyqtSignal +from PyQt6.QtGui import QPixmap, QColor import numpy as np import logging -from config import MONSTER_COUNT, MONSTER_DATA, FIELD_FEATURE_COUNT +from src.core.config import MONSTER_COUNT, MONSTER_DATA, FIELD_FEATURE_COUNT +from src.core.paths import image_path, resource_path logger = logging.getLogger(__name__) @@ -17,8 +31,10 @@ class InputPanelUI(QFrame): # Signals to communicate with the main application predict_requested = pyqtSignal() reset_requested = pyqtSignal() - input_changed = pyqtSignal() # Signal emitted when any monster input changes - terrain_changed = pyqtSignal(list) # New signal for terrain changes + input_changed = ( + pyqtSignal() + ) # Signal emitted when any monster input changes + terrain_changed = pyqtSignal(list) # New signal for terrain changes # Terrain display mapping terrain_display_mapping = { @@ -33,7 +49,7 @@ class InputPanelUI(QFrame): "crossbow_top_crossbow": "顶部弩炮", "fire_side_crossbow": "侧边弩炮", "fire_side_fire": "侧边火炮", - "fire_top_fire": "顶部火炮" + "fire_top_fire": "顶部火炮", } @staticmethod @@ -43,17 +59,19 @@ def get_terrain_feature_columns(): """ try: # 加载类别映射 - class_map_path = "tools/battlefield_recognize/class_to_idx.json" - with open(class_map_path, 'r', encoding='utf-8') as f: + class_map_path = resource_path( + "tools", "battlefield_recognize", "class_to_idx.json" + ) + with open(class_map_path, "r", encoding="utf-8") as f: class_to_idx = json.load(f) # 使用与data_cleaning_with_field_recognize_gpu.py相同的逻辑 grouped_elements = defaultdict(list) for class_name in class_to_idx.keys(): - if class_name.endswith('_none'): + if class_name.endswith("_none"): continue - condensed_name = re.sub(r'_left_', '_', class_name) - condensed_name = re.sub(r'_right_', '_', condensed_name) + condensed_name = re.sub(r"_left_", "_", class_name) + condensed_name = re.sub(r"_right_", "_", condensed_name) grouped_elements[condensed_name].append(class_name) # 返回排序后的特征列名 @@ -67,14 +85,21 @@ def __init__(self): super().__init__() self.left_monsters: dict[str, str] = {} self.right_monsters: dict[str, str] = {} - self.terrain_buttons = {} # Initialize terrain buttons + self.terrain_buttons = {} # Initialize terrain buttons self.terrain_feature_columns = self.get_terrain_feature_columns() if not self.terrain_feature_columns: # If fetching fails, use default terrain features self.terrain_feature_columns = [ - "altar_vertical", "block_parallel", "block_vertical_altar", - "block_vertical_block", "coil_narrow", "coil_wide", - "crossbow_top", "fire_side_left", "fire_side_right", "fire_top" + "altar_vertical", + "block_parallel", + "block_vertical_altar", + "block_vertical_block", + "coil_narrow", + "coil_wide", + "crossbow_top", + "fire_side_left", + "fire_side_right", + "fire_top", ] self.init_ui() @@ -82,16 +107,16 @@ def __init__(self): def init_ui(self): self.setObjectName("input_panel_id") - self.setStyleSheet( - """ + self.setStyleSheet(""" QWidget#input_panel_id { background-color: rgba(0, 0, 0, 40); border-radius: 15px; border: 5px solid #F5EA2D; } - """ + """) + self.setSizePolicy( + QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding ) - self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) self.main_layout = QVBoxLayout(self) self.main_layout.setContentsMargins(0, 0, 0, 0) @@ -102,8 +127,7 @@ def init_ui(self): # 创建滚动区域 scroll = QScrollArea() scroll.setWidgetResizable(True) - scroll.setStyleSheet( - """ + scroll.setStyleSheet(""" QScrollBar:horizontal { background: rgba(0, 0, 0, 0); width: 12px; /* 宽度 */ @@ -147,8 +171,7 @@ def init_ui(self): min-height: 20px; border-radius: 6px; } - """ - ) + """) scroll_content = QWidget() self.scroll_grid = QGridLayout(scroll_content) @@ -205,7 +228,7 @@ def init_ui(self): # 分两行显示按钮,每行6个,更好地利用空间 terrain_rows = [] - for row_idx in range(2): # Changed from 3 to 2 rows + for row_idx in range(2): # Changed from 3 to 2 rows row_terrain = QWidget() row_layout = QHBoxLayout(row_terrain) row_layout.setSpacing(3) @@ -213,13 +236,14 @@ def init_ui(self): terrain_rows.append((row_terrain, row_layout)) for i, terrain_key in enumerate(self.terrain_feature_columns): - display_name = self.terrain_display_mapping.get(terrain_key, terrain_key) + display_name = self.terrain_display_mapping.get( + terrain_key, terrain_key + ) btn = QPushButton(display_name) btn.setCheckable(True) btn.setFixedHeight(26) - btn.setStyleSheet( - """ + btn.setStyleSheet(""" QPushButton { background-color: #7B7B7B; color: #FAFAFA; @@ -242,13 +266,16 @@ def init_ui(self): color: #313131; padding: 0px; } - """ + """) + btn.clicked.connect( + lambda checked, k=terrain_key: self.on_terrain_multi_selected( + k + ) ) - btn.clicked.connect(lambda checked, k=terrain_key: self.on_terrain_multi_selected(k)) self.terrain_buttons[terrain_key] = btn # 分两行显示,每行6个按钮 - row_index = i // 6 # Changed from 4 to 6 buttons per row + row_index = i // 6 # Changed from 4 to 6 buttons per row if row_index < len(terrain_rows): terrain_rows[row_index][1].addWidget(btn) else: @@ -271,8 +298,7 @@ def init_ui(self): # 预测按钮 - 带样式 self.predict_button = QPushButton("开始预测") self.predict_button.clicked.connect(self.predict_requested.emit) - self.predict_button.setStyleSheet( - """ + self.predict_button.setStyleSheet(""" QPushButton { background-color: #313131; color: #F3F31F; @@ -287,14 +313,14 @@ def init_ui(self): QPushButton:pressed { background-color: #212121; } - """ + """) + self.predict_button.setSizePolicy( + QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed ) - self.predict_button.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) self.reset_button = QPushButton("重置") self.reset_button.clicked.connect(self.reset_entries) - self.reset_button.setStyleSheet( - """ + self.reset_button.setStyleSheet(""" QPushButton { background-color: #313131; color: #F3F31F; @@ -309,8 +335,7 @@ def init_ui(self): QPushButton:pressed { background-color: #212121; } - """ - ) + """) result_button_layout.addWidget(self.predict_button) result_button_layout.addWidget(self.reset_button) @@ -346,7 +371,9 @@ def load_images(self): img_label.setAlignment(Qt.AlignmentFlag.AlignCenter) try: - pixmap = QPixmap(f"images/{MONSTER_DATA['原始名称'][i]}.png") + pixmap = QPixmap( + str(image_path(MONSTER_DATA['原始名称'][i])) + ) if not pixmap.isNull(): pixmap = pixmap.scaled( 60, @@ -371,7 +398,9 @@ def load_images(self): left_entry.setFixedWidth(60) left_entry.setPlaceholderText("左") left_entry.setAlignment(Qt.AlignmentFlag.AlignCenter) - left_entry.textChanged.connect(self.input_changed.emit) # Connect to signal + left_entry.textChanged.connect( + self.input_changed.emit + ) # Connect to signal self.left_monsters[str(i)] = left_entry # 右输入框 (放在左输入框下方) @@ -379,16 +408,26 @@ def load_images(self): right_entry.setFixedWidth(60) right_entry.setPlaceholderText("右") right_entry.setAlignment(Qt.AlignmentFlag.AlignCenter) - right_entry.textChanged.connect(self.input_changed.emit) # Connect to signal + right_entry.textChanged.connect( + self.input_changed.emit + ) # Connect to signal self.right_monsters[str(i)] = right_entry # 添加到容器 - container_layout.addWidget(img_label, 0, Qt.AlignmentFlag.AlignCenter) - container_layout.addWidget(left_entry, 0, Qt.AlignmentFlag.AlignCenter) - container_layout.addWidget(right_entry, 0, Qt.AlignmentFlag.AlignCenter) + container_layout.addWidget( + img_label, 0, Qt.AlignmentFlag.AlignCenter + ) + container_layout.addWidget( + left_entry, 0, Qt.AlignmentFlag.AlignCenter + ) + container_layout.addWidget( + right_entry, 0, Qt.AlignmentFlag.AlignCenter + ) # 添加到网格布局 - self.scroll_grid.addWidget(monster_container, row, col, Qt.AlignmentFlag.AlignCenter) + self.scroll_grid.addWidget( + monster_container, row, col, Qt.AlignmentFlag.AlignCenter + ) # 更新行列位置 col += 1 @@ -423,14 +462,18 @@ def set_monster_counts(self, left_counts: dict, right_counts: dict): if monster_id in self.left_monsters: self.left_monsters[monster_id].setText(str(count)) if count > 0: - self.left_monsters[monster_id].setStyleSheet("background-color: yellow;") + self.left_monsters[monster_id].setStyleSheet( + "background-color: yellow;" + ) else: self.left_monsters[monster_id].setStyleSheet("") for monster_id, count in right_counts.items(): if monster_id in self.right_monsters: self.right_monsters[monster_id].setText(str(count)) if count > 0: - self.right_monsters[monster_id].setStyleSheet("background-color: yellow;") + self.right_monsters[monster_id].setStyleSheet( + "background-color: yellow;" + ) else: self.right_monsters[monster_id].setStyleSheet("") self.input_changed.emit() @@ -458,7 +501,9 @@ def get_selected_terrains(self): def build_terrain_features(self, left_counts, right_counts): """构建包含地形的完整特征向量(支持多选地形)""" # Use the actual number of terrain feature columns - num_field_features = len(self.terrain_feature_columns) if FIELD_FEATURE_COUNT else 0 + num_field_features = ( + len(self.terrain_feature_columns) if FIELD_FEATURE_COUNT else 0 + ) # Build terrain feature vector terrain_features = np.zeros(num_field_features) @@ -472,7 +517,9 @@ def build_terrain_features(self, left_counts, right_counts): if terrain_idx < num_field_features: terrain_features[terrain_idx] = 1 else: - logger.warning(f"Terrain {terrain} not in feature columns: {self.terrain_feature_columns}") + logger.warning( + f"Terrain {terrain} not in feature columns: {self.terrain_feature_columns}" + ) logger.debug(f"Selected terrains: {selected_terrains}") logger.debug(f"Terrain feature vector: {terrain_features}") @@ -483,11 +530,13 @@ def build_terrain_features(self, left_counts, right_counts): # 1R-61R (Right monster features) # 62R-73R (Field features R, copied) - full_features = np.concatenate([ - left_counts, # 1L-61L (Left monster features) - terrain_features, # 62L-73L (Field features L) - right_counts, # 1R-61R (Right monster features) - terrain_features # 62R-73R (Field features R, copied) - ]) + full_features = np.concatenate( + [ + left_counts, # 1L-61L (Left monster features) + terrain_features, # 62L-73L (Field features L) + right_counts, # 1R-61R (Right monster features) + terrain_features, # 62R-73R (Field features R, copied) + ] + ) return full_features diff --git a/simular_history_match_ui.py b/src/ui/similar_history_match_ui.py similarity index 85% rename from simular_history_match_ui.py rename to src/ui/similar_history_match_ui.py index 6ddf18c..dd8753b 100644 --- a/simular_history_match_ui.py +++ b/src/ui/similar_history_match_ui.py @@ -1,11 +1,20 @@ from PyQt6.QtCore import Qt -from PyQt6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QScrollArea, QGraphicsDropShadowEffect, QFrame -from PyQt6.QtGui import QPixmap, QImage, QFont, QIcon, QPainter, QColor +from PyQt6.QtWidgets import ( + QWidget, + QVBoxLayout, + QHBoxLayout, + QLabel, + QScrollArea, + QGraphicsDropShadowEffect, + QFrame, +) +from PyQt6.QtGui import QPixmap, QColor import numpy as np import logging -from similar_history_match import HistoryMatch -from config import MONSTER_COUNT, MONSTER_DATA +from src.analysis.similar_history_match import HistoryMatch +from src.core.config import MONSTER_COUNT, MONSTER_DATA +from src.core.paths import image_path logger = logging.getLogger(__name__) @@ -24,8 +33,7 @@ def init_ui(self): self.history_scroll_area = QScrollArea() self.history_scroll_area.setFixedWidth(540) self.history_scroll_area.setWidgetResizable(True) - self.history_scroll_area.setStyleSheet( - """ + self.history_scroll_area.setStyleSheet(""" QScrollBar:horizontal { background: rgba(0, 0, 0, 0); width: 12px; /* 宽度 */ @@ -70,8 +78,7 @@ def init_ui(self): min-height: 20px; border-radius: 6px; } - """ - ) + """) # 创建内容部件 self.history_widget = QWidget() @@ -114,26 +121,28 @@ def render_similar_matches(self, left_monsters, right_monsters): shadow.setOffset(2) # 偏移量(0表示均匀四周发光) title_label.setGraphicsEffect(shadow) - title_label.setStyleSheet( - """ + title_label.setStyleSheet(""" QWidget { border-radius: 0px; font-size: 24px; font-weight: bold; color: white; } - """ - ) + """) self.history_layout.addWidget(title_label) # 渲染每个历史对局 for idx in top_indices: - self.add_history_match(idx, sims[idx], left_monsters, right_monsters) + self.add_history_match( + idx, sims[idx], left_monsters, right_monsters + ) except Exception as e: logger.error(f"渲染历史对局失败: {str(e)}") - def add_history_match(self, idx, similarity, left_monsters, right_monsters): + def add_history_match( + self, idx, similarity, left_monsters, right_monsters + ): """添加单个历史对局到面板""" # 获取历史数据 left = self.history_match.past_left[idx] @@ -159,25 +168,23 @@ def add_history_match(self, idx, similarity, left_monsters, right_monsters): setR_past = set(np.where(right > 0)[0]) # 判断是否需要镜像历史对局 - should_swap = (len(setL_cur ^ setR_past) + len(setR_cur ^ setL_past)) < ( - len(setL_cur ^ setL_past) + len(setR_cur ^ setR_past) - ) + should_swap = ( + len(setL_cur ^ setR_past) + len(setR_cur ^ setL_past) + ) < (len(setL_cur ^ setL_past) + len(setR_cur ^ setR_past)) # 获取地形名称 terrain_name = self.history_match.get_terrain_names(idx, should_swap) # 创建对局容器 match_widget = QWidget() - match_widget.setStyleSheet( - """ + match_widget.setStyleSheet(""" QWidget { background-color: rgba(50, 50, 50, 150); border-radius: 10px; padding: 0px; margin: 5px; } - """ - ) + """) match_widget.setFixedSize(500, 170) # 增加高度以容纳地形信息 match_layout = QVBoxLayout(match_widget) @@ -199,8 +206,7 @@ def add_history_match(self, idx, similarity, left_monsters, right_monsters): # 添加地形信息显示 terrain_label = QLabel(f"地形: {terrain_name}") - terrain_label.setStyleSheet( - """ + terrain_label.setStyleSheet(""" QLabel { color: #CCCCCC; font: 10px Microsoft YaHei; @@ -209,8 +215,7 @@ def add_history_match(self, idx, similarity, left_monsters, right_monsters): border-radius: 3px; margin: 2px; } - """ - ) + """) terrain_label.setAlignment(Qt.AlignmentFlag.AlignCenter) match_layout.addWidget(terrain_label) @@ -219,16 +224,14 @@ def add_history_match(self, idx, similarity, left_monsters, right_monsters): def create_team_widget(self, side, counts, is_winner): """创建单个队伍显示部件""" team_widget = QWidget() - team_widget.setStyleSheet( - f""" + team_widget.setStyleSheet(f""" QWidget {{ background-color: {'rgba(250, 250, 50, 150)' if is_winner else 'rgba(50, 50, 50, 100)'}; border-radius: 8px; padding: 0px; margin: 0px; }} - """ - ) + """) layout = QVBoxLayout(team_widget) @@ -240,16 +243,14 @@ def create_team_widget(self, side, counts, is_winner): shadow01.setOffset(3) # 偏移量(0表示均匀四周发光) ops_widget.setGraphicsEffect(shadow01) - ops_widget.setStyleSheet( - """ + ops_widget.setStyleSheet(""" QWidget { background-color: rgba(0, 0, 0, 0); border-radius: 0px; padding: 0px; margin: 0px; } - """ - ) + """) ops_layout = QHBoxLayout(ops_widget) ops_layout.setSpacing(5) ops_layout.setContentsMargins(0, 0, 0, 0) @@ -258,7 +259,9 @@ def create_team_widget(self, side, counts, is_winner): if count > 0: # 创建干员显示 op_widget = QWidget() - op_widget.setStyleSheet("background-color: rgba(0, 0, 0, 0); padding: 0px 0;margin: 0px;") + op_widget.setStyleSheet( + "background-color: rgba(0, 0, 0, 0); padding: 0px 0;margin: 0px;" + ) op_layout = QVBoxLayout(op_widget) op_layout.setContentsMargins(0, 0, 0, 0) op_layout.setAlignment(Qt.AlignmentFlag.AlignCenter) @@ -268,10 +271,15 @@ def create_team_widget(self, side, counts, is_winner): img_label.setFixedSize(60, 60) img_label.setAlignment(Qt.AlignmentFlag.AlignCenter) try: - pixmap = QPixmap(f"images/{MONSTER_DATA['原始名称'][i+1]}.png") + pixmap = QPixmap( + str(image_path(MONSTER_DATA['原始名称'][i + 1])) + ) if not pixmap.isNull(): pixmap = pixmap.scaled( - 60, 60, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation + 60, + 60, + Qt.AspectRatioMode.KeepAspectRatio, + Qt.TransformationMode.SmoothTransformation, ) img_label.setPixmap(pixmap) except: @@ -280,13 +288,11 @@ def create_team_widget(self, side, counts, is_winner): # 数量标签 count_label = QLabel(str(int(count))) count_label.setAlignment(Qt.AlignmentFlag.AlignCenter) - count_label.setStyleSheet( - """ + count_label.setStyleSheet(""" color: #EDEDED; font: bold 20px SimHei; min-width: 20px; - """ - ) + """) op_layout.addWidget(img_label, stretch=3) op_layout.addWidget(count_label, stretch=1) diff --git a/src/utils/__init__.py b/src/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/unit.py b/src/utils/unit.py similarity index 94% rename from unit.py rename to src/utils/unit.py index d4c290c..8e2be97 100644 --- a/unit.py +++ b/src/utils/unit.py @@ -1,4 +1,4 @@ -from constants import UNIT_CONFIG # 添加这行导入 +from src.core.constants import UNIT_CONFIG class Unit: def __init__(self, team, unit_id, x, y): diff --git a/tools/battlefield_composite/extract_webm_frames.py b/tools/battlefield_composite/extract_webm_frames.py deleted file mode 100644 index ca85e6a..0000000 --- a/tools/battlefield_composite/extract_webm_frames.py +++ /dev/null @@ -1,92 +0,0 @@ -import logging -from pathlib import Path -import subprocess -import os - - -monster_name = { - "弧光锋卫长" : "Arc_Frontliner_Leader", - "炮击组长" : "Mortar_Gunner_Leader", - "复仇者" : "Hateful_Avenger", - "重装防御者" : "Heavy_Defender", - "“庞贝”" : "Pompeii", - "冰原术师" : "Icefield_Caster", - "沸血骑士团精锐" : "Bloodboil_Knightclub_Elite", - "高塔术师" : "Spire_Caster", - "固海凿石者" : "Ocean_Stonecutter", - # "呼啸骑士团学徒" : "Roar_Knightclub_Trainee", - "湖畔志愿者" : "Lakeside_Volunteer", - "杰斯顿·威廉姆斯" : "Jesselton_Williams", - "酸液源石虫·α" : "Acid_Originium_Slug_α", - "神射手囚犯" : "Elite_Sniper_Prisoner", - "拳手囚犯" : "Pugilist_Prisoner", - "染污躯壳" : "Tainted_Carcass", - "泥岩巨像" : "Mudrock_Colossus", - "狂暴的猎狗pro" : "Rabid_Hound_Pro", - "宿主拾荒者" : "Possessed_Veteran_Junkman", - "狂暴宿主组长" : "Enraged_Possessed_Leader", - "萨卡兹大剑手" : "Sarkaz_Greatswordsman", - "矿脉守卫" : "Vein_Guardian", - "山海众窥魅人" : "Shanhaizhong_Seer", - "提亚卡乌好战者" : "Tiacauh_Fanatic", - "码头水手" : "Dockworker", - "变异巨岩蛛" : "Mutant_Giant_Rock_Spider", - "萨卡兹子裔链术师" : "Sarkaz_Heirbearer_Chain_Caster", - "温顺的武装驮兽" : "Armored_Burdenbeast", - "木裂战士" : "Shattered_Champion", - "深溟裂礁者" : "Nethersea_Reefbreaker", - "山雪鬼" : "Tschäggättä", - "高普尼克" : "Gopnik", - "冰爆源石虫" : "Infused_Glacial_Originium_Slug", - "反装甲步兵" : "Anti-Armor_Infantry", - "“钳钳生风”" : "Consortium_of_Pincers", - "富营养的穿刺者" : "Nourished_Piercer", - "高级武装人员" : "Senior_Armed_Militant", - "朗姆酒推荐者" : "Rum_Connoisseur", - "烈酒级醒酒助手" : "Whiskey-Grade_Waker-Upper", - "萨卡兹王庭军术师" : "Sarkaz_Royal_Court_Caster", - # "灰尾香主" : "Greytail_Leader", - "阵地击人手" : "Field_Bludgeoner", - "源石畸变体" : "Originiutant", - "提亚卡乌破坏王" : "Tiacauh_Annihilator", - "逐腐兽" : "Rotchaser", - "高能源石虫" : "Infused_Originium_Slug", - "风情街“星术师”" : "Fashion_Street_Stellar_Caster", - "田鼷力士" : "Fieldmus_Bruiser", - "残党萨克斯手" : "Remnant_Saxophonist", - "散华骑士团学徒" : "Nova_Knightclub_Trainee", - "“阿咬”" : "Bitey", - # "“门”" : "", - "“投石机”" : "Catapult", - # "“复仇者”" : '"Hateful Avenger"', - # "扎罗,“狼之主”" : "Zaaro", - # "“自在”" : "Free", - # "灼热源石虫" : "Blazing_Originium_Slug", - # "萨卡兹子裔责罚者" : "Sarkaz_Heirbearer_Punisher", - # "" : "", -} - -def extract_webm(webm_path: Path, output_folder: Path): - extract_webm_cmd = f"ffmpeg -c:v libvpx -i \"{webm_path}\" \"{output_folder}/frame%d.png\"" - subprocess.run(extract_webm_cmd, shell=True, check=True) - - -def main(): - webm_folder = "./tools/battlefield_composite/monster_images" - for webm_path in Path(webm_folder).glob("*.webm"): - name_split = webm_path.parts[-1].split("-") - print(name_split) - if (name := monster_name.get(name_split[0])) is not None: - output_directory = webm_path.parent / (name + "-" + name_split[3]) - print(output_directory) - if not output_directory.exists(): - output_directory.mkdir(exist_ok=True) - extract_webm(webm_path, output_directory) - else: - logging.warning(f"{output_directory} already exists") - else: - logging.error(f"Name: {name_split[0]} not found!") - - -if __name__ == "__main__": - main() diff --git a/tools/data_washer_new.py b/tools/data_washer_new.py deleted file mode 100644 index 3f10486..0000000 --- a/tools/data_washer_new.py +++ /dev/null @@ -1,904 +0,0 @@ -import logging -from pathlib import Path -import cv2 -import numpy as np -import time -import csv -import os -import sys -import recognize -import tqdm - -MONSTER_NUM=56 -black_list_rows = [] - -def merge(nums): - if not nums: - return "" - intervals = [] - start = end = nums[0] - for num in nums[1:]: - if num == end + 1: - end = num - else: - intervals.append((start, end)) - start = end = num - intervals.append((start, end)) # 添加最后一个区间 - - parts = [] - for s, e in intervals: - if s == e: - parts.append(str(s)) - else: - parts.append(f"{s}-{e}") - return ','.join(parts) - -def is_continuous_sublist(sub, main): - return any(sub == main[i:i+len(sub)] for i in range(len(main) - len(sub) + 1)) - -def remove_duplicate_subsequences_easy(listdata, threshold=3): - record = [] - for i in range(len(listdata)-threshold-1): - if is_continuous_sublist(listdata[i+1:i+threshold+2], listdata[:i+threshold+1]): - record.extend(list(range(i+1,i+threshold+2))) - reco_last = list(set(record)) - reco_last.sort() - processed_data = [listdata[j] for j in range(len(listdata)) if j not in reco_last] - return processed_data, reco_last - -def remove_duplicate_subsequences(arr, threshold=3): - """ - 处理二维数组版本,将每行视为独立元素,避免内存溢出 - :param arr: 二维np数组,形状为(N, D) - :param threshold: 需要删除的连续重复子序列最小长度 - :return: 去重后的数组,被删除的索引列表 - """ - if arr.ndim != 2: - raise ValueError("输入必须是二维数组") - - n = arr.shape[0] - print(arr[9]) - # 哈希化每行以便快速比较 - dtype = np.dtype((np.void, arr.dtype.itemsize * arr.shape[1])) - hashed_arr = np.ascontiguousarray(arr).view(dtype).flatten() - - # 初始化滚动数组和列最大值记录 - prev_row = np.zeros(n, dtype=int) - max_per_col = np.zeros(n, dtype=int) - targ = 10 - for i in range(n): - # 生成当前行比较掩码 - equal_mask = (hashed_arr[i] == hashed_arr) - - # 计算当前行DP值 - curr_row = np.zeros(n, dtype=int) - curr_row[0] = equal_mask[0] # 处理j=0 - - if i > 0: - # 向量化计算j>=1的情况 - curr_row[1:] = np.where(equal_mask[1:], prev_row[:-1] + 1, 0) - - # 更新列最大值(只考虑i<=j的情况) - col_mask = np.arange(n) > i - max_per_col = np.maximum(max_per_col, curr_row * col_mask) - - # 滚动更新 - prev_row = curr_row - if i*100/n >= targ: - print("检查重复数据,处理进度: {:.1f}%: ".format(i*100/n)) - targ += 10 - - # 确定有效列并生成删除索引 - valid_cols = np.where(max_per_col >= threshold)[0] - to_remove = set() - - for j in valid_cols: - length = max_per_col[j] - start = max(0, j - length) - to_remove.update(range(start, j+1)) - - final_indices = sorted(to_remove) - return np.delete(arr, final_indices, axis=0), final_indices - -def isfloat(value): - try: - float(value) - return True - except ValueError: - return False - - -def read_and_remove_zeros(filename,MONSTER_NUM=56): - ''' - 输入数据文件名 - 输出去0和空之后的数组,和删去的行号列表 - ''' - data = [] - datafull = [] - row_id = 0 - kong = [] - short = [] - lines_num = MONSTER_NUM*2 - with open(filename, 'r') as file: - csv_reader = csv.reader(file) - for row in csv_reader: - if len(row) < lines_num + 1: - short.append(row_id) - data.append([0]*lines_num) - datafull.append([0]*lines_num) - # 分离数字部分和末尾的字母 - elif isfloat(row[0]) and '' not in row[:lines_num]: - numbers = list(map(int,map(float, row[:lines_num]))) # 转换为整数列表 - vals = row[lines_num:] - datafull.append(numbers+vals) - data.append(numbers) - else: - kong.append(row_id) - data.append([0]*lines_num) - datafull.append([0]*lines_num) - #把这一行转化为全0行暂时录入 - row_id += 1 - print('原数据总长度:',len(datafull)) - print('数据长度过短的行:',merge(short)) - print('含有不合法数据的行:',merge(kong)) - - np_array = np.array(data) - # 去除全零行 - all_zeros = [] - for i in range(np_array.shape[0]): - if np.all(np_array[i][MONSTER_NUM:] == 0) or np.all(np_array[i][:MONSTER_NUM] == 0): - all_zeros.append(i) - #np.delete(np_array, all_zeros, axis=0) - - data_new = [datafull[j] for j in range(len(datafull)) if j not in all_zeros] - - all_zeros_idx = [i for i in all_zeros if i not in kong+short] - print('一侧数据全为0的行:',merge(all_zeros_idx)) - print('筛选后数据总长度:',len(data_new)) - return data_new,all_zeros,len(datafull) - -def do_duplicate(listdata): - if listdata == []: - return [],[] - num_data = [list(map(int,map(float, i[:MONSTER_NUM*2]))) for i in listdata] - np_num_data = np.array(num_data) - _,remove_list = remove_duplicate_subsequences(np_num_data, threshold=3) - #print(remove_list) - result = [listdata[j] for j in range(len(listdata)) if j not in remove_list] - return result,remove_list - -def ori_pos(n,del1,del2): - remaining_after_first = [i for i in range(n) if i not in del1] - second_deleted_original = [remaining_after_first[t] for t in del2] - all_deleted = del1 + second_deleted_original - all_deleted.sort() - return all_deleted,second_deleted_original - -def view_monster_counts(listdata): - if listdata == []: - return True,[],[] - wrong_counts = [] - num_left = [list(map(int,map(float, i[:MONSTER_NUM]))) for i in listdata] - num_right = [list(map(int,map(float, i[MONSTER_NUM:MONSTER_NUM*2]))) for i in listdata] - print(len(num_left[0]),len(num_right[0])) - black_listed = False - MONSTER_MIN = 0 - MONSTER_MAX = 100 - MONSTER_LIMIT = { - 0:[range(0,100),'狗',False], - 1:[range(0,50),'红虫',False], - 2:[range(0,30),'大盾',False], - 3:[range(0,30),'大剑',False], - 6:[range(0,6),'庞贝',False], - 8:[range(0,4),'石头人',False], - 27:[range(0,4),'杰斯顿',False], - 28:[[0],'自在',True], - 29:[[0],'狼主',True], - 30:[[0],'雷德',True] #三大boss全设为0 - } - ind = 0 - for i1,i2 in zip(num_left,num_right): - for x1 in i1: - if x1 < MONSTER_MIN: - print(f'{ind}行左侧发现小于0的数据!') - if ind not in wrong_counts: - wrong_counts.append(ind) - if x1 > MONSTER_MAX: - print(f'{ind}行左侧发现大于100的数据!') - if ind not in wrong_counts: - wrong_counts.append(ind) - for x2 in i2: - if x2 < MONSTER_MIN: - print(f'{ind}行右侧发现小于0的数据!') - if ind not in wrong_counts: - wrong_counts.append(ind) - if x2 > MONSTER_MAX: - print(f'{ind}行右侧发现大于100的数据!') - if ind not in wrong_counts: - wrong_counts.append(ind) - for j in MONSTER_LIMIT: - if i1[j] not in MONSTER_LIMIT[j][0]: - print(f'{ind}行左侧发现{MONSTER_LIMIT[j][1]},数量:{i1[j]}') - if ind not in wrong_counts: - wrong_counts.append(ind) - if MONSTER_LIMIT[j][2] and not black_listed: - black_listed = True - print(f'确认为30人数据,文档加入黑名单。') - if i2[j] not in MONSTER_LIMIT[j][0]: - print(f'{ind}行右侧发现{MONSTER_LIMIT[j][1]},数量:{i2[j]}') - if ind not in wrong_counts: - wrong_counts.append(ind) - if MONSTER_LIMIT[j][2] and not black_listed: - black_listed = True - print(f'确认为30人数据,文档加入黑名单。') - ind += 1 - processed_data = [listdata[j] for j in range(len(listdata)) if j not in wrong_counts] - mwdata = is_list_true_np(processed_data) - processed_data2 = [processed_data[j] for j in range(len(processed_data)) if j not in mwdata] - print(f'怪物信息不符合权重分配的数据行:{mwdata}') - return black_listed,wrong_counts,mwdata,processed_data2 - -def del_duplicate_by_time(listdata,delete_no_time = True): - ind = 0 - no_time = [] - timedata = [] - wrong_time = [] - for i in listdata: - if len(i) < MONSTER_NUM*2+2 or i[-1] == 'N/A': - no_time.append(ind) - ind += 1 - print(merge(no_time),'行:未发现时间戳!') - data_with_time = [listdata[j] for j in range(len(listdata)) if j not in no_time] - ind = 0 - for i in data_with_time: - if i[-1] not in timedata: - timedata.append(i[-1]) - else: - wrong_time.append(ind) - print(f'{timedata.index(i[-1])}行与{ind}行发现同名截图,文件名:{i[-1]}') - ind += 1 - if not delete_no_time: - #先找到wrongtime元素的原始位置,再从原列表删除 - remaining_after_first = [i for i in range(len(listdata)) if i not in no_time] - second_deleted_original = [remaining_after_first[t] for t in wrong_time] - data_with_time_ok = [listdata[j] for j in range(len(listdata)) if j not in second_deleted_original] - wrong_time = second_deleted_original - else: - data_with_time_ok = [data_with_time[j] for j in range(len(data_with_time)) if j not in wrong_time] - return data_with_time_ok, no_time, wrong_time - -def savecsv(listdata,outputfile): - # 处理数字转换 - processed = [] - for row in listdata: - new_row = [] - for item in row: - if isinstance(item, (int, float)): - new_row.append(int(item)) - else: - new_row.append(item) - processed.append(new_row) - - # 写入CSV文件 - with open(outputfile, 'w', newline='') as f: - csv.writer(f).writerows(processed) - print(f'已保存至{outputfile}') - - -def find_csv_files(root_dir): - csv_files = [] - for root, dirs, files in os.walk(root_dir): - for file in files: - if file.lower().endswith('.csv'): - csv_path = os.path.join(root, file) - csv_files.append(csv_path) - return csv_files - -def easydata2data(easydata): - # 测试用函数 - # easydata格式:[[[序号,数量][序号,数量][序号,数量]],[[序号,数量][序号,数量][序号,数量]],结果],没有的序号和数量留-1,序号是真实序号 - datalist = [0]*MONSTER_NUM*2 - for i in easydata[0]: - if i[0] > 0: - datalist[i[0]-1] = i[1] - for i in easydata[1]: - if i[0] > 0: - datalist[i[0]-1+MONSTER_NUM] = i[1] - datalist.extend(easydata[2]) - return datalist - -def find_where_from(easydata,floder_path): - # easydata格式:[[[序号,数量][序号,数量][序号,数量]],[[序号,数量][序号,数量][序号,数量]],结果],没有的序号和数量留-1,序号是真实序号 - datalist = [0]*MONSTER_NUM*2 - from_list = [] - for i in easydata[0]: - if i[0] > 0: - datalist[i[0]-1] = i[1] - for i in easydata[1]: - if i[0] > 0: - datalist[i[0]-1+MONSTER_NUM] = i[1] - datalist.extend(easydata[2]) - print(datalist) - csvlist = find_csv_files(floder_path) - for c in csvlist: - print(c) - for c in csvlist: - print(f'正在检查:{c}…………………………') - lines_num = MONSTER_NUM*2 - datafull = [] - row_id = 0 - with open(c, 'r') as file: - csv_reader = csv.reader(file) - for row in csv_reader: - if isfloat(row[0]) and '' not in row[:lines_num] and len(row) > lines_num: - numbers = list(map(int,map(float, row[:lines_num]))) # 转换为整数列表 - vals = [row[lines_num]] - if datalist == numbers+vals: - from_list.append([c,row_id+1]) - print(f'数据来源于:{c},第{row_id+1}行!') - row_id += 1 - str_from = '' - if from_list != []: - str_from = "\n".join([i[0] + '第:' + str(i[1]) + '行' for i in from_list]) - print(f'可能的数据来源:{str_from}') - -def is_distance_not_over_60(a,b,c,d): - #a, b = interval1 - #c, d = interval2 - # 判断区间是否有交集 - if max(a, c) <= min(b, d): - return True # 有交集时距离为0,未超过60 - # 计算不重叠时的间隔 - if b < c: - distance = c - b # interval1在左,interval2在右 - else: - distance = a - d # interval2在左,interval1在右 - return distance <= 60 - -def is_list_true_np(fulllist): - cost_list = [ - [2,0], [2,0.1], [7,0], [7,0], [3,0.1], [10,0], [25,15], [22,0], [25,100], [7,0], - [5,0.2], [7,0], [2,0], [15,2], [13,0], [12,1.5], - [6,0.2], [18,3], [15,3], [18,1], [11,0], [10,1], [16,0], [5,0.5], [15,2], [14,0], - [30,0], [35,100], [-1,-1], [-1,-1], [-1,-1], [6,0.5], [6,0], - [16,0], [15,5], [11,0], [26,0], [15,0], [4,0.1], [10,0], [21,0], [5,0.2], - [18,0], [9,1.5], [8,0.5], [16,0], [21,0], [7,0], [36,10], [10,2], [30,15], - [25,0], [27,0], [32,6], [25,50], [15,5] - ] - round_cost_list = [[50,70],[70,90],[90,110],[110,130],[120,160],[140,180],[160,200],[170,230],[190,250],[210,270]] - - # Convert to numpy arrays - cost_arr = np.array(cost_list) - round_cost_arr = np.array(round_cost_list) - round_low = round_cost_arr[:, 0] - round_high = round_cost_arr[:, 1] - - # Validity mask for cost entries not equal to [-1, -1] - valid_mask = np.all(cost_arr != [-1, -1], axis=1) - - # Split the input into left and right parts - fulllist_np = np.array([i[:112] for i in fulllist], dtype=np.float64) - N = fulllist_np.shape[0] - left_part = fulllist_np[:, :56] - right_part = fulllist_np[:, 56:112] - - # Calculate valid entries (cost not [-1,-1] and count >0) - valid_left = valid_mask[np.newaxis, :] & (left_part > 0) - valid_right = valid_mask[np.newaxis, :] & (right_part > 0) - - # Compute mincostL and maxcostL for left - left_min_terms = ( - (left_part - 1) * cost_arr[np.newaxis, :, 0] + - ((left_part - 1) * (left_part - 2) * cost_arr[np.newaxis, :, 1]) / 2 + - 0.01 - ) * valid_left - mincostL = left_min_terms.sum(axis=1) - - left_max_terms = ( - (left_part + 1) * cost_arr[np.newaxis, :, 0] + - (left_part * (left_part + 1) * cost_arr[np.newaxis, :, 1]) / 2 - - 0.01 - ) * valid_left - maxcostL = left_max_terms.sum(axis=1) - - # Compute mincostR and maxcostR for right - right_min_terms = ( - (right_part - 1) * cost_arr[np.newaxis, :, 0] + - ((right_part - 1) * (right_part - 2) * cost_arr[np.newaxis, :, 1]) / 2 + - 0.01 - ) * valid_right - mincostR = right_min_terms.sum(axis=1) - - right_max_terms = ( - (right_part + 1) * cost_arr[np.newaxis, :, 0] + - (right_part * (right_part + 1) * cost_arr[np.newaxis, :, 1]) / 2 - - 0.01 - ) * valid_right - maxcostR = right_max_terms.sum(axis=1) - - # Check overlap with round costs - left_low = np.maximum(mincostL[:, np.newaxis], round_low) - left_high = np.minimum(maxcostL[:, np.newaxis], round_high) - left_cond = left_low <= left_high - - right_low = np.maximum(mincostR[:, np.newaxis], round_low) - right_high = np.minimum(maxcostR[:, np.newaxis], round_high) - right_cond = right_low <= right_high - - both_cond = left_cond & right_cond - any_round = np.any(both_cond, axis=1) - - # Get indices where no round condition is satisfied - false_indices = np.where(~any_round)[0].tolist() - return false_indices - -def is_list_true(onelist): - roundlist = [] - #费用和附加费用,写死在代码里吧,不想读文件了。 - cost_list = [[2,0],[2,0.1],[7,0],[7,0],[3,0.1],[10,0],[25,15],[22,0],[25,100],[7,0],[5,0.2],[7,0],[2,0],[15,2],[13,0],[12,1.5], - [6,0.2],[18,3],[15,3],[18,1],[11,0],[10,1],[16,0],[5,0.5],[15,2],[14,0],[30,0],[35,100],[-1,-1],[-1,-1],[-1,-1],[6,0.5],[6,0], - [16,0],[15,5],[11,0],[26,0],[15,0],[4,0.1],[10,0],[21,0],[5,0.2],[18,0],[9,1.5],[8,0.5],[16,0],[21,0],[7,0],[36,10],[10,2],[30,15], - [25,0],[27,0],[32,6],[25,50],[15,5]] - # - round_cost_list = [[50,70],[70,90],[90,110],[110,130],[120,160],[140,180],[160,200],[170,230],[190,250],[210,270]] - left = onelist[:MONSTER_NUM] - right = onelist[MONSTER_NUM:MONSTER_NUM*2] - #result = onelist[MONSTER_NUM*2] - #print(left,right,result) - mincostL = sum([(left[i]-1)*cost_list[i][0]+(left[i]-1)*(left[i]-2)*cost_list[i][1]/2+0.01 for i in range(len(left)) if (cost_list[i] != [-1,-1]) and (left[i] > 0)]) - maxcostL = sum([(left[i]+1)*cost_list[i][0]+left[i]*(left[i]+1)*cost_list[i][1]/2-0.01 for i in range(len(left)) if (cost_list[i] != [-1,-1]) and (left[i] > 0)]) - mincostR = sum([(right[i]-1)*cost_list[i][0]+(right[i]-1)*(right[i]-2)*cost_list[i][1]/2+0.01 for i in range(len(right)) if (cost_list[i] != [-1,-1]) and (right[i] > 0)]) - maxcostR = sum([(right[i]+1)*cost_list[i][0]+right[i]*(right[i]+1)*cost_list[i][1]/2-0.01 for i in range(len(right)) if (cost_list[i] != [-1,-1]) and (right[i] > 0)]) - print(mincostL,maxcostL,mincostR,maxcostR) - for i in range(len(round_cost_list)): - if max(mincostL, round_cost_list[i][0]) <= min(maxcostL, round_cost_list[i][1]) and max(mincostR, round_cost_list[i][0]) <= min(maxcostR, round_cost_list[i][1]): - roundlist.append(i) - if roundlist != []: - return True - else: - print(f'{onelist}is not true!!!') - return False - #return is_distance_not_over_60(mincostL,maxcostL,mincostR,maxcostR) - -def recognize_review(data,img_floder,matched_threshold = 0.1,ocr_threshold = 0.5): - print("正在进行识别数据检查") - print("data行数:",len(data)) - ref_row = [0] * (recognize.MONSTER_COUNT * 2) - need_delete = [False] * len(data) - for idx, row in tqdm.tqdm(enumerate(data), total=len(data), desc="Processing rows"): - ref_row = [0] * (recognize.MONSTER_COUNT * 2) - try: - img_name = row[recognize.MONSTER_COUNT * 2 + 1] - img_path = img_floder / Path(img_name) - if not img_path.exists(): - print(f"未找到对应的图像: {img_name} ") - continue - img = cv2.imread(img_path) - main_roi = ((0, 0), (img.shape[1], img.shape[0])) - results = recognize.process_regions(main_roi, img,matched_threshold,ocr_threshold) - # 处理结果 - for res in results: - if "error" in res: - print(f"识别失败 行号: {idx}, 图片: {img_name}, 错误类型: {res['error']}", file=sys.stderr) - break - if res["matched_id"]: - if res["region_id"] < 3: - ref_row[res["matched_id"] - 1] = int(res["number"]) - else: - ref_row[res["matched_id"] - 1 + MONSTER_NUM] = int(res["number"]) - else: - # 检查数据行是否与参考行匹配 - data_row = row[0 : recognize.MONSTER_COUNT * 2] - if data_row != ref_row: - print(f"找到不匹配的数据行: {idx} 行,对应图片文件: {img_name}", file=sys.stderr) - print(f"识别结果 : {ref_row}", file=sys.stderr) - print(f"文件数据 : {data_row}", file=sys.stderr) - need_delete[idx] = True - else: - need_delete[idx] = False - except Exception as e: - logging.exception(f"Error processing line {idx}", e) - need_delete[idx] = True - newdata = [row for idx, row in enumerate(data) if not need_delete[idx]] - deleted = [idx for idx, del_flag in enumerate(need_delete) if del_flag] - return newdata, deleted - -#newdata,deleted,ori_len = read_and_remove_zeros('0502.csv',MONSTER_NUM=56) -#_,inc = remove_duplicate_subsequences() -#print('数据例:',newdata[:3]) -#result,deleted2 = do_duplicate(newdata) -#print(deleted,deleted2) -#dt = ori_pos(ori_len,deleted,deleted2) -#print(dt) -#print('筛选后数据总长度:',len(result)) -#view_monster_counts(newdata) -#del_duplicate_by_time(newdata) - -def process_full(filename,do_remove_duplicate_subsequences = False,delete_no_time = True,open_black_list = True,re_recognize_imgs = False,img_floder = '',matched_threshold=0.1, ocr_threshold=0.5): - wrong_type_list = [] - newdata,deleted0,ori_len = read_and_remove_zeros(filename,MONSTER_NUM=56) - deleted1 = [] - if do_remove_duplicate_subsequences: - newdata,deleted1 = do_duplicate(newdata) - newdata, deleted2, deleted3 = del_duplicate_by_time(newdata,delete_no_time) - if not delete_no_time: - deleted2 = [] - black_listed,deleted4,deleted5,newdata = view_monster_counts(newdata) - deleted6 = [] - if re_recognize_imgs: - newdata, deleted6 = recognize_review(newdata,img_floder,matched_threshold, ocr_threshold) - deleted7 = [] - if open_black_list: - newdata,deleted7 = process_black_list(newdata) - - dl = deleted0 - flag = 0 - for i in [deleted1,deleted2,deleted3,deleted4,deleted5,deleted6,deleted7]: - if i != []: - dl,secori = ori_pos(ori_len,dl,i) - if flag == 0: - wrong_type_list.append(['不合法的数据:',merge([i + 1 for i in deleted0])]) - wrong_type_list.append(['重复出现的连续数据*:',merge([i + 1 for i in secori])]) - elif flag == 1: - wrong_type_list.append(['未包含时间轴的数据:',merge([i + 1 for i in secori])]) - elif flag == 2: - wrong_type_list.append(['时间轴信息重复的数据:',merge([i + 1 for i in secori])]) - elif flag == 3: - wrong_type_list.append(['怪物信息错误的数据:',merge([i + 1 for i in secori])]) - elif flag == 4: - wrong_type_list.append(['不符合出怪权重规则的数据:',merge([i + 1 for i in secori])]) - elif flag == 5: - wrong_type_list.append(['经图片识别错误的数据*:',merge([i + 1 for i in secori])]) - elif flag == 6: - wrong_type_list.append(['黑名单内数据:',merge([i + 1 for i in secori])]) - flag += 1 - return black_listed,newdata,dl,wrong_type_list - - -def test1(): - black_listed,newdata,dl,wrong_type_list = process_full('0502processed.csv') - dllist = [i + 1 for i in dl] - print(f'删除了{dllist}行的数据') - for i in wrong_type_list: - print(i) - savecsv(newdata,'0502processed2.csv') - -def process_floder(flodername,savefilename,lastsavefilename,do_remove_duplicate_subsequences = True,delete_no_time = True,open_black_list = True,re_recognize_imgs = False,img_floder = '',matched_threshold=0.1, ocr_threshold=0.5): - ''' - 输入: - flodername:需要处理的文件夹名 - savefilename:全部整合保存到的文件名(不进行总去重) - lastsavefilename:全部整合并去重保存到的最终文件名 - do_remove_duplicate_subsequences:是否清理连续3个以上重复元素的重复序列 - delete_no_time:是否删除没有时间戳的数据行 - ''' - global black_list_rows - full_data_list = [] - csvlist = find_csv_files(flodername) - for csv in csvlist: - print(csv) - for csv in csvlist: - print(f'正在处理:{csv}…………………………') - black_listed,newdata,dl,wrong_type_list = process_full(csv,do_remove_duplicate_subsequences,delete_no_time,open_black_list,re_recognize_imgs,img_floder,matched_threshold, ocr_threshold) - dllist = [i + 1 for i in dl] - print(f'删除了{merge(dllist)}行的数据') - for i in wrong_type_list: - print(i) - if not black_listed: - #未进黑名单则合并至全部数据 - full_data_list += newdata - else: - print(f'该数据为30人局数据,自动进入黑名单,不计入总数据!') - if len(newdata) < 5000:#不是整合数据 - black_list_rows += newdata - savecsv(full_data_list,savefilename) - black_listed,newdata,dl,wrong_type_list = process_full(savefilename,do_remove_duplicate_subsequences,delete_no_time,open_black_list,re_recognize_imgs,img_floder,matched_threshold, ocr_threshold) - #保存后再总处理去重 - dllist = [i + 1 for i in dl] - print(f'删除了{merge(dllist)}行的数据') - for i in wrong_type_list: - print(i) - savecsv(newdata,lastsavefilename) - -def process_black_list(full_data): - #黑名单里所有的数据检测到重复的就删 - global black_list_rows - delete_rows = [] - ok_data = [] - idx = 0 - for i in full_data: - if i in black_list_rows: - delete_rows.append(idx) - else: - ok_data.append(i) - idx += 1 - print(f'黑名单内数据:{merge(delete_rows)}') - return ok_data,delete_rows - - -def process_file(filename,savefilename,do_remove_duplicate_subsequences = True,delete_no_time = True,open_black_list = True,re_recognize_imgs = False,img_floder = '',matched_threshold=0.1, ocr_threshold=0.5): - ''' - 输入: - filename:需要处理的文件名 - savefilename:处理后保存到的文件名 - do_remove_duplicate_subsequences:是否清理连续3个以上重复元素的重复序列 - delete_no_time:是否删除没有时间戳的数据行 - ''' - black_listed,newdata,dl,wrong_type_list = process_full(filename,do_remove_duplicate_subsequences,delete_no_time,open_black_list,re_recognize_imgs,img_floder,matched_threshold, ocr_threshold) - #保存后再总处理去重 - dllist = [i + 1 for i in dl] - print(f'删除了{merge(dllist)}行的数据') - for i in wrong_type_list: - print(i) - savecsv(newdata,savefilename) - - -#process_floder(r'D:\Backup\Downloads\arcdata','arcdata_fullaa.csv','arcdata_full_washed_plus.csv') - -import tkinter as tk -from tkinter import ttk, filedialog, messagebox -import sys -import threading -import queue - -class RedirectText(object): - def __init__(self, text_widget, log_file="processing.log"): - self.text_widget = text_widget - self.log_file = log_file - self.queue = queue.Queue() - self.root = text_widget.master - self.lock = threading.Lock() - - # 初始化日志文件 - self.setup_logfile() - - def setup_logfile(self): - try: - # 使用追加模式打开日志文件 - self.log_fd = open(self.log_file, "a", encoding="utf-8") - except Exception as e: - self.log_fd = None - self.write(f"无法打开日志文件: {str(e)}\n") - - def write(self, message): - # 写入日志文件(带线程锁) - with self.lock: - if self.log_fd: - try: - self.log_fd.write(message) - self.log_fd.flush() # 确保立即写入磁盘 - except Exception as e: - self.log_fd = None - self.queue.put(f"日志写入失败: {str(e)}\n") - - # 写入队列供界面显示 - self.queue.put(message) - self.root.after(100, self.update_text) - - def update_text(self): - while not self.queue.empty(): - msg = self.queue.get_nowait() - self.text_widget.insert(tk.END, msg) - self.text_widget.see(tk.END) - - def flush(self): - pass - - def close_logfile(self): - with self.lock: - if self.log_fd: - self.log_fd.close() - self.log_fd = None - -class ProcessingThread(threading.Thread): - def __init__(self, func, args=(), kwargs={}, callback=None): - super().__init__() - self.func = func - self.args = args - self.kwargs = kwargs - self.callback = callback - self.daemon = True - self.exception = None - - def run(self): - try: - self.func(*self.args, **self.kwargs) - except Exception as e: - self.exception = e - finally: - if self.callback: - self.callback(self.exception) - - -def create_gui(): - root = tk.Tk() - root.title("数据搅拌机") - root.geometry("800x600") - - # 在此处定义关闭事件处理函数(推荐位置) - def on_close(): - sys.stdout.close_logfile() # 关闭日志文件 - if messagebox.askokcancel("退出", "确定要退出程序吗?"): # 添加确认对话框 - root.destroy() # 销毁窗口 - - # 绑定关闭事件处理 - root.protocol("WM_DELETE_WINDOW", on_close) - - # 创建文本输出区域 - output_text = tk.Text(root, wrap=tk.WORD) - output_text.grid(row=3, column=0, columnspan=2, padx=10, pady=10, sticky="nsew") - sys.stdout = RedirectText(output_text, "data_processing.log") - - # 处理文件夹的Frame - folder_frame = ttk.LabelFrame(root, text="处理文件夹(处理文件夹及其所有子文件夹下的CSV文件,并合并为一个)") - folder_frame.grid(row=0, column=0, padx=10, pady=5, sticky="ew") - - # 处理文件夹的组件 - ttk.Label(folder_frame, text="选择文件夹:").grid(row=0, column=0, padx=5, sticky="w") - folder_path = tk.StringVar() - folder_entry = ttk.Entry(folder_frame, textvariable=folder_path, width=40) - folder_entry.grid(row=0, column=1, padx=5) - ttk.Button(folder_frame, text="浏览", command=lambda: folder_path.set(filedialog.askdirectory())).grid(row=0, column=2, padx=5) - - ttk.Label(folder_frame, text="中间保存文件(不进行最终去重):").grid(row=1, column=0, padx=5, sticky="w") - interim_save = tk.StringVar() - ttk.Entry(folder_frame, textvariable=interim_save, width=40).grid(row=1, column=1, padx=5) - ttk.Button(folder_frame, text="浏览", command=lambda: interim_save.set(filedialog.asksaveasfilename(defaultextension=".csv",filetypes=[("CSV文件", "*.csv"), ("所有文件", "*.*")]))).grid(row=1, column=2, padx=5) - - ttk.Label(folder_frame, text="最终保存文件:").grid(row=2, column=0, padx=5, sticky="w") - final_save = tk.StringVar() - ttk.Entry(folder_frame, textvariable=final_save, width=40).grid(row=2, column=1, padx=5) - ttk.Button(folder_frame, text="浏览", command=lambda: final_save.set(filedialog.asksaveasfilename(defaultextension=".csv",filetypes=[("CSV文件", "*.csv"), ("所有文件", "*.*")]))).grid(row=2, column=2, padx=5) - - # 复选框 - remove_dup = tk.BooleanVar(value=False) - ttk.Checkbutton(folder_frame, text="不依赖时间戳清理重复子序列(在大数据集会非常慢,通常关闭)", variable=remove_dup).grid(row=3, column=0, columnspan=3, sticky="w") - - del_time = tk.BooleanVar(value=True) - ttk.Checkbutton(folder_frame, text="删除无时间戳数据", variable=del_time).grid(row=4, column=0, columnspan=3, sticky="w") - - open_black = tk.BooleanVar(value=True) - ttk.Checkbutton(folder_frame, text="将黑名单文件内的所有数据行同时加入黑名单", variable=open_black).grid(row=5, column=0, columnspan=3, sticky="w") - - # 修改后的图片识别行(将复选框和阈值输入放在同一行) - re_recognize_var = tk.BooleanVar(value=False) - ttk.Checkbutton(folder_frame, text="启用图片二次识别(必须指定图片路径)", variable=re_recognize_var).grid(row=6, column=0, padx=5, sticky="w") - - # 添加匹配阈值设置 - ttk.Label(folder_frame, text="匹配阈值:").grid(row=6, column=1, padx=(20,5), sticky="e") - matched_threshold_var = tk.DoubleVar(value=0.1) - ttk.Entry(folder_frame, textvariable=matched_threshold_var, width=6).grid(row=6, column=2, sticky="w") - - # 添加OCR阈值设置 - ttk.Label(folder_frame, text="OCR阈值:").grid(row=6, column=3, padx=(20,5), sticky="e") - ocr_threshold_var = tk.DoubleVar(value=0.5) - ttk.Entry(folder_frame, textvariable=ocr_threshold_var, width=6).grid(row=6, column=4, sticky="w") - - # 调整后续行号(原row=6改为row=7开始) - ttk.Label(folder_frame, text="图片文件夹路径:").grid(row=7, column=0, padx=5, sticky="w") - img_folder_path = tk.StringVar() - ttk.Entry(folder_frame, textvariable=img_folder_path, width=40).grid(row=7, column=1, padx=5) - ttk.Button(folder_frame, text="浏览", command=lambda: img_folder_path.set(filedialog.askdirectory())).grid(row=7, column=2, padx=5) - - # 调整处理文件夹按钮的行号 - - - # 处理文件夹按钮 - folder_button = ttk.Button(folder_frame, text="执行处理") - folder_button.grid(row=8, column=0, columnspan=5, pady=5) - - # 处理文件的Frame - file_frame = ttk.LabelFrame(root, text="处理单个文件") - file_frame.grid(row=1, column=0, padx=10, pady=5, sticky="ew") - - # 处理文件的组件 - ttk.Label(file_frame, text="选择文件:").grid(row=0, column=0, padx=5, sticky="w") - file_path = tk.StringVar() - ttk.Entry(file_frame, textvariable=file_path, width=40).grid(row=0, column=1, padx=5) - ttk.Button(file_frame, text="浏览", command=lambda: file_path.set(filedialog.askopenfilename())).grid(row=0, column=2, padx=5) - - ttk.Label(file_frame, text="保存路径:").grid(row=1, column=0, padx=5, sticky="w") - save_path = tk.StringVar() - ttk.Entry(file_frame, textvariable=save_path, width=40).grid(row=1, column=1, padx=5) - ttk.Button(file_frame, text="浏览", command=lambda: save_path.set(filedialog.asksaveasfilename(defaultextension=".csv",filetypes=[("CSV文件", "*.csv"), ("所有文件", "*.*")]))).grid(row=1, column=2, padx=5) - - # 复选框 - remove_dup_file = tk.BooleanVar(value=False) - ttk.Checkbutton(file_frame, text="不依赖时间戳清理重复子序列(在大数据集会非常慢,通常关闭)", variable=remove_dup_file).grid(row=2, column=0, columnspan=3, sticky="w") - - del_time_file = tk.BooleanVar(value=True) - ttk.Checkbutton(file_frame, text="删除无时间戳数据", variable=del_time_file).grid(row=3, column=0, columnspan=3, sticky="w") - - open_black_file = tk.BooleanVar(value=True) - ttk.Checkbutton(file_frame, text="将黑名单文件内的所有数据行同时加入黑名单", variable=open_black_file).grid(row=4, column=0, columnspan=3, sticky="w") - - # 修改后的图片识别行 - re_recognize_file_var = tk.BooleanVar(value=False) - ttk.Checkbutton(file_frame, text="启用图片二次识别(必须指定图片路径)", variable=re_recognize_file_var).grid(row=5, column=0, padx=5, sticky="w") - - # 匹配阈值 - ttk.Label(file_frame, text="匹配阈值:").grid(row=5, column=1, padx=(20,5), sticky="e") - matched_threshold_file_var = tk.DoubleVar(value=0.1) - ttk.Entry(file_frame, textvariable=matched_threshold_file_var, width=6).grid(row=5, column=2, sticky="w") - - # OCR阈值 - ttk.Label(file_frame, text="OCR阈值:").grid(row=5, column=3, padx=(20,5), sticky="e") - ocr_threshold_file_var = tk.DoubleVar(value=0.5) - ttk.Entry(file_frame, textvariable=ocr_threshold_file_var, width=6).grid(row=5, column=4, sticky="w") - - # 调整后续行号 - ttk.Label(file_frame, text="图片文件夹路径:").grid(row=6, column=0, padx=5, sticky="w") - img_folder_file_path = tk.StringVar() - ttk.Entry(file_frame, textvariable=img_folder_file_path, width=40).grid(row=6, column=1, padx=5) - ttk.Button(file_frame, text="浏览", command=lambda: img_folder_file_path.set(filedialog.askdirectory())).grid(row=6, column=2, padx=5) - - # 调整处理文件按钮的行号 - - - - # 处理文件按钮 - file_button = ttk.Button(file_frame, text="执行处理") - file_button.grid(row=7, column=0, columnspan=5, pady=5) - - # 配置网格权重 - root.grid_rowconfigure(3, weight=1) - root.grid_columnconfigure(0, weight=1) - - # 按钮回调函数 - def process_folder_wrapper(): - folder = folder_path.get() - interim = interim_save.get() - final = final_save.get() - if not folder or not interim or not final: - messagebox.showerror("错误", "请填写所有路径") - return - - folder_button.config(state=tk.DISABLED) - def callback(e): - folder_button.config(state=tk.NORMAL) - if e: - messagebox.showerror("错误", str(e)) - else: - messagebox.showinfo("完成", "文件夹处理完成") - - thread = ProcessingThread( - func=process_floder, - args=(folder, interim, final, remove_dup.get(), del_time.get(), open_black.get(),re_recognize_var.get(), Path(img_folder_path.get()),matched_threshold_var.get(), ocr_threshold_var.get()), - callback=callback - ) - thread.start() - - def process_file_wrapper(): - input_file = file_path.get() - output_file = save_path.get() - if not input_file or not output_file: - messagebox.showerror("错误", "请填写所有路径") - return - - file_button.config(state=tk.DISABLED) - def callback(e): - file_button.config(state=tk.NORMAL) - if e: - messagebox.showerror("错误", str(e)) - else: - messagebox.showinfo("完成", "文件处理完成") - - thread = ProcessingThread( - func=process_file, - args=(input_file, output_file, remove_dup_file.get(), del_time_file.get(), open_black_file.get(),re_recognize_file_var.get(), Path(img_folder_file_path.get()),matched_threshold_file_var.get(), ocr_threshold_file_var.get()), - callback=callback - ) - thread.start() - - # 绑定按钮命令 - folder_button.config(command=process_folder_wrapper) - file_button.config(command=process_file_wrapper) - - return root - -if __name__ == "__main__": - # 请确保以下函数已经正确导入或定义: - # process_floder, process_file, find_csv_files, savecsv - - app = create_gui() - app.mainloop() diff --git a/uv.lock b/uv.lock index 6c882ef..ad093bc 100644 --- a/uv.lock +++ b/uv.lock @@ -2,68 +2,68 @@ version = 1 revision = 3 requires-python = ">=3.10" resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", @@ -94,6 +94,12 @@ resolution-markers = [ conflicts = [[ { package = "cannotmax-greenvine", extra = "cpu" }, { package = "cannotmax-greenvine", extra = "cu128" }, +], [ + { package = "cannotmax-greenvine", extra = "cpu" }, + { package = "cannotmax-greenvine", extra = "cu130" }, +], [ + { package = "cannotmax-greenvine", extra = "cu128" }, + { package = "cannotmax-greenvine", extra = "cu130" }, ]] [[package]] @@ -113,45 +119,52 @@ sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/3e/38/7859ff463 [[package]] name = "cannotmax-greenvine" -version = "1.0.8" +version = "2.0.0" source = { virtual = "." } dependencies = [ { name = "maafw" }, { name = "matplotlib" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "onnxruntime", version = "1.24.3", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "onnxruntime", version = "1.25.0", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "onnxruntime", version = "1.24.3", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "onnxruntime", version = "1.25.0", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "opencv-python" }, - { name = "pandas", version = "2.3.3", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "pandas", version = "3.0.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "pandas", version = "2.3.3", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "pandas", version = "3.0.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "pillow" }, { name = "pyqt6" }, { name = "pywin32" }, { name = "rapidocr" }, - { name = "scikit-learn", version = "1.7.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "scikit-learn", version = "1.8.0", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "scikit-learn", version = "1.7.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "scikit-learn", version = "1.8.0", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "toml" }, { name = "windows-capture" }, ] [package.optional-dependencies] cpu = [ - { name = "torch", version = "2.11.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(python_full_version < '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version >= '3.11' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "torch", version = "2.11.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(python_full_version < '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu')" }, - { name = "torchvision", version = "0.26.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(python_full_version < '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version >= '3.11' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "torchvision", version = "0.26.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(python_full_version < '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu')" }, + { name = "torch", version = "2.11.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "torch", version = "2.11.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "torchvision", version = "0.26.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "torchvision", version = "0.26.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] cu128 = [ { name = "torch", version = "2.11.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" } }, { name = "torchvision", version = "0.26.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" } }, ] +cu130 = [ + { name = "torch", version = "2.11.0+cu130", source = { registry = "https://download.pytorch.org/whl/cu130" } }, + { name = "torchvision", version = "0.26.0+cu130", source = { registry = "https://download.pytorch.org/whl/cu130" } }, +] [package.dev-dependencies] dev = [ { name = "onnx" }, { name = "onnxscript" }, + { name = "pandas-stubs", version = "2.3.3.260113", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "pandas-stubs", version = "3.0.0.260204", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "pyinstaller" }, + { name = "types-toml" }, ] [package.metadata] @@ -170,17 +183,21 @@ requires-dist = [ { name = "toml" }, { name = "torch", marker = "extra == 'cpu'", index = "https://download.pytorch.org/whl/cpu", conflict = { package = "cannotmax-greenvine", extra = "cpu" } }, { name = "torch", marker = "extra == 'cu128'", specifier = ">=2.7.0", index = "https://download.pytorch.org/whl/cu128", conflict = { package = "cannotmax-greenvine", extra = "cu128" } }, + { name = "torch", marker = "extra == 'cu130'", specifier = ">=2.7.0", index = "https://download.pytorch.org/whl/cu130", conflict = { package = "cannotmax-greenvine", extra = "cu130" } }, { name = "torchvision", marker = "extra == 'cpu'", index = "https://download.pytorch.org/whl/cpu", conflict = { package = "cannotmax-greenvine", extra = "cpu" } }, { name = "torchvision", marker = "extra == 'cu128'", specifier = ">=0.22.0", index = "https://download.pytorch.org/whl/cu128", conflict = { package = "cannotmax-greenvine", extra = "cu128" } }, + { name = "torchvision", marker = "extra == 'cu130'", specifier = ">=0.22.0", index = "https://download.pytorch.org/whl/cu130", conflict = { package = "cannotmax-greenvine", extra = "cu130" } }, { name = "windows-capture" }, ] -provides-extras = ["cpu", "cu128"] +provides-extras = ["cpu", "cu128", "cu130"] [package.metadata.requires-dev] dev = [ { name = "onnx", specifier = ">=1.19.0" }, { name = "onnxscript", specifier = ">=0.7.0" }, + { name = "pandas-stubs" }, { name = "pyinstaller", specifier = ">=6.15.0" }, + { name = "types-toml" }, ] [[package]] @@ -311,7 +328,7 @@ name = "colorlog" version = "6.10.1" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "colorama", marker = "sys_platform == 'win32' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/a2/61/f083b5ac52e505dfc1c624eafbf8c7589a0d7f32daa398d2e7590efa5fda/colorlog-6.10.1.tar.gz", hash = "sha256:eb4ae5cb65fe7fec7773c2306061a8e63e02efc2c72eba9d27b0fa23c94f1321" } wheels = [ @@ -323,17 +340,17 @@ name = "contourpy" version = "1.3.2" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54" } wheels = [ @@ -400,62 +417,62 @@ name = "contourpy" version = "1.3.3" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", @@ -482,7 +499,7 @@ resolution-markers = [ "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880" } wheels = [ @@ -563,6 +580,18 @@ wheels = [ name = "cuda-bindings" version = "12.9.6" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +resolution-markers = [ + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.11' and platform_machine != 's390x'", + "python_full_version < '3.11' and platform_machine == 's390x'", +] dependencies = [ { name = "cuda-pathfinder", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, ] @@ -590,6 +619,46 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/4b/98/8e5363d00c959d4172b1d619a4f03af454bf9952636224f0ac0f5c35c067/cuda_bindings-12.9.6-cp314-cp314t-win_amd64.whl", hash = "sha256:7f0a08eba6e807d041bf6f2ba66d84db1ddf54787399dfac716497ef40fb5fc3" }, ] +[[package]] +name = "cuda-bindings" +version = "13.2.0" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +resolution-markers = [ + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.11' and platform_machine != 's390x'", + "python_full_version < '3.11' and platform_machine == 's390x'", +] +dependencies = [ + { name = "cuda-pathfinder", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, +] +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/1a/fe/7351d7e586a8b4c9f89731bfe4cf0148223e8f9903ff09571f78b3fb0682/cuda_bindings-13.2.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08b395f79cb89ce0cd8effff07c4a1e20101b873c256a1aeb286e8fd7bd0f556" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/aa/ef/184aa775e970fc089942cd9ec6302e6e44679d4c14549c6a7ea45bf7f798/cuda_bindings-13.2.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6f3682ec3c4769326aafc67c2ba669d97d688d0b7e63e659d36d2f8b72f32d6" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/ec/ea/81999d01375645f34596c76eb046b4b36d58cc6fe2bddb2410f8a7b7a827/cuda_bindings-13.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:845025438a1b9e20718b9fb42add3e0eb72e85458bcab3eeb80bfd8f0a9dab33" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/e0/a9/3a8241c6e19483ac1f1dcf5c10238205dcb8a6e9d0d4d4709240dff28ff4/cuda_bindings-13.2.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:721104c603f059780d287969be3d194a18d0cc3b713ed9049065a1107706759d" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/e9/94/2748597f47bb1600cd466b20cab4159f1530a3a33fe7f70fee199b3abb9e/cuda_bindings-13.2.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1eba9504ac70667dd48313395fe05157518fd6371b532790e96fbb31bbb5a5e1" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/29/5a/0ce1731c48bcd9f40996a4ef1abbf634f1a7fe4a15c5050b1e75ce3a7acf/cuda_bindings-13.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:debb51b211d246f8326f6b6e982506a5d0d9906672c91bc478b66addc7ecc60a" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/52/c8/b2589d68acf7e3d63e2be330b84bc25712e97ed799affbca7edd7eae25d6/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e865447abfb83d6a98ad5130ed3c70b1fc295ae3eeee39fd07b4ddb0671b6788" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/1f/92/f899f7bbb5617bb65ec52a6eac1e9a1447a86b916c4194f8a5001b8cde0c/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46d8776a55d6d5da9dd6e9858fba2efcda2abe6743871dee47dd06eb8cb6d955" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/bb/a5/d7f01a415e134546248cef612adad8153c9f1eb10ec79505a7cd8294370b/cuda_bindings-13.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:45815daeb595bf3b405c52671a2542b1f8e9329f3b029494acbfcc74aeaa1f2d" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/df/93/eef988860a3ca985f82c4f3174fc0cdd94e07331ba9a92e8e064c260337f/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6629ca2df6f795b784752409bcaedbd22a7a651b74b56a165ebc0c9dcbd504d0" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/18/23/6db3aba46864aee357ab2415135b3fe3da7e9f1fa0221fa2a86a5968099c/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7dca0da053d3b4cc4869eff49c61c03f3c5dbaa0bcd712317a358d5b8f3f385d" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/c4/84/d3b6220b51cbc02ca14db7387e97445126b4ff5125aaa6c5dd7dcb75e679/cuda_bindings-13.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:8cebe3ce4aeeca5af9c490e175f76c4b569bbf4a35a62294b777bc77bf7ac4d8" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/c0/87/87a014f045b77c6de5c8527b0757fe644417b184e5367db977236a141602/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6464b30f46692d6c7f65d4a0e0450d81dd29de3afc1bb515653973d01c2cd6e" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/ee/5e/c0fe77a73aaefd3fff25ffaccaac69c5a63eafdf8b9a4c476626ef0ac703/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4af9f3e1be603fa12d5ad6cfca7844c9d230befa9792b5abdf7dd79979c3626" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/e3/73/98bcb069778fe420226db75aff54b5dd6c3ecfd0912edabab723326e80b7/cuda_bindings-13.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:bd658bb5c0e55b7b3e5dd0ed509c6addb298c665db26a9bfba35e1e626000ba2" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/5f/58/ed2c3b39c8dd5f96aa7a4abef0d47a73932c7a988e30f5fa428f00ed0da1/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df850a1ff8ce1b3385257b08e47b70e959932f5f432d0a4e46a355962b4e4771" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/1f/01/0c941b112ceeb21439b05895eace78ca1aa2eaaf695c8521a068fd9b4c00/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8a16384c6494e5485f39314b0b4afb04bee48d49edb16d5d8593fd35bbd231b" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/52/49/4e01cc06447d39476e138d1b1adec8d35c0d04eccd2c8d69befc08cd66e8/cuda_bindings-13.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6ccf14e0c1def3b7200100aafff3a9f7e210ecb6e409329e92dcf6cd2c00d5c7" }, +] + [[package]] name = "cuda-pathfinder" version = "1.5.3" @@ -602,6 +671,18 @@ wheels = [ name = "cuda-toolkit" version = "12.8.1" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +resolution-markers = [ + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.11' and platform_machine != 's390x'", + "python_full_version < '3.11' and platform_machine == 's390x'", +] wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/d4/c8/7dce3a0b15b42a3b58e7d96eb22a687d3bf2c44e01d149a6874629cd9938/cuda_toolkit-12.8.1-py2.py3-none-any.whl", hash = "sha256:adc7906af4ecbf9a352f9dca5734eceb21daec281ccfcf5675e1d2f724fc2cba" }, ] @@ -641,6 +722,61 @@ nvtx = [ { name = "nvidia-nvtx-cu12", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, ] +[[package]] +name = "cuda-toolkit" +version = "13.0.2" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +resolution-markers = [ + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.11' and platform_machine != 's390x'", + "python_full_version < '3.11' and platform_machine == 's390x'", +] +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/57/b2/453099f5f3b698d7d0eab38916aac44c7f76229f451709e2eb9db6615dcd/cuda_toolkit-13.0.2-py2.py3-none-any.whl", hash = "sha256:b198824cf2f54003f50d64ada3a0f184b42ca0846c1c94192fa269ecd97a66eb" }, +] + +[package.optional-dependencies] +cublas = [ + { name = "nvidia-cublas", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +cudart = [ + { name = "nvidia-cuda-runtime", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +cufft = [ + { name = "nvidia-cufft", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +cufile = [ + { name = "nvidia-cufile", marker = "sys_platform == 'linux'" }, +] +cupti = [ + { name = "nvidia-cuda-cupti", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +curand = [ + { name = "nvidia-curand", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +cusolver = [ + { name = "nvidia-cusolver", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +cusparse = [ + { name = "nvidia-cusparse", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +nvjitlink = [ + { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +nvrtc = [ + { name = "nvidia-cuda-nvrtc", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +nvtx = [ + { name = "nvidia-nvtx", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] + [[package]] name = "cycler" version = "0.12.1" @@ -901,8 +1037,8 @@ version = "5.10.2" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ { name = "maaagentbinary" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "strenum" }, ] wheels = [ @@ -919,7 +1055,7 @@ name = "macholib" version = "1.16.4" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ - { name = "altgraph", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu') or sys_platform == 'darwin' or (sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu') or (sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "altgraph", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu') or sys_platform == 'darwin' or (sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu') or (sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/10/2f/97589876ea967487978071c9042518d28b958d87b17dceb7cdc1d881f963/macholib-1.16.4.tar.gz", hash = "sha256:f408c93ab2e995cd2c46e34fe328b130404be143469e41bc366c807448979362" } wheels = [ @@ -1016,13 +1152,13 @@ name = "matplotlib" version = "3.10.9" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ - { name = "contourpy", version = "1.3.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "contourpy", version = "1.3.3", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "contourpy", version = "1.3.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "contourpy", version = "1.3.3", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "cycler" }, { name = "fonttools" }, { name = "kiwisolver" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "packaging" }, { name = "pillow" }, { name = "pyparsing" }, @@ -1091,8 +1227,8 @@ name = "ml-dtypes" version = "0.5.4" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/0e/4a/c27b42ed9b1c7d13d9ba8b6905dece787d6259152f2309338aed29b2447b/ml_dtypes-0.5.4.tar.gz", hash = "sha256:8ab06a50fb9bf9666dd0fe5dfb4676fa2b0ac0f31ecff72a6c3af8e22c063453" } wheels = [ @@ -1146,12 +1282,14 @@ name = "networkx" version = "3.4.2" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1" } wheels = [ @@ -1163,62 +1301,86 @@ name = "networkx" version = "3.6.1" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509" } wheels = [ @@ -1230,12 +1392,12 @@ name = "numpy" version = "2.2.6" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] @@ -1302,62 +1464,62 @@ name = "numpy" version = "2.4.4" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", @@ -1458,6 +1620,16 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/04/74/f4c001f4714c3ad9ce037e18cf2b9c64871a84951eaa0baf683a9ca9301c/numpy-2.4.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f2cf083b324a467e1ab358c105f6cad5ea950f50524668a80c486ff1db24e119" }, ] +[[package]] +name = "nvidia-cublas" +version = "13.1.0.3" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/e1/a5/fce49e2ae977e0ccc084e5adafceb4f0ac0c8333cb6863501618a7277f67/nvidia_cublas-13.1.0.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:c86fc7f7ae36d7528288c5d88098edcb7b02c633d262e7ddbb86b0ad91be5df2" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/e7/44/423ac00af4dd95a5aeb27207e2c0d9b7118702149bf4704c3ddb55bb7429/nvidia_cublas-13.1.0.3-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:ee8722c1f0145ab246bccb9e452153b5e0515fd094c3678df50b2a0888b8b171" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/10/f5/f50bc3f5c2bb57ab8f5b4d78bc1146b57810d42cb8fcb28cbe2e14050376/nvidia_cublas-13.1.0.3-py3-none-win_amd64.whl", hash = "sha256:2a3b94a37def342471c59fad7856caee4926809a72dd5270155d6a31b5b277be" }, +] + [[package]] name = "nvidia-cublas-cu12" version = "12.8.4.1" @@ -1468,6 +1640,16 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/70/61/7d7b3c70186fb651d0fbd35b01dbfc8e755f69fd58f817f3d0f642df20c3/nvidia_cublas_cu12-12.8.4.1-py3-none-win_amd64.whl", hash = "sha256:47e9b82132fa8d2b4944e708049229601448aaad7e6f296f630f2d1a32de35af" }, ] +[[package]] +name = "nvidia-cuda-cupti" +version = "13.0.85" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/2a/2a/80353b103fc20ce05ef51e928daed4b6015db4aaa9162ed0997090fe2250/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_aarch64.whl", hash = "sha256:796bd679890ee55fb14a94629b698b6db54bcfd833d391d5e94017dd9d7d3151" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/33/6d/737d164b4837a9bbd202f5ae3078975f0525a55730fe871d8ed4e3b952b0/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_x86_64.whl", hash = "sha256:4eb01c08e859bf924d222250d2e8f8b8ff6d3db4721288cf35d14252a4d933c8" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/ad/df/b74b10025c1205695c5676373f2edd3e87a7202cc62ead0dfbc373b0f6ea/nvidia_cuda_cupti-13.0.85-py3-none-win_amd64.whl", hash = "sha256:683f58d301548deeefcb8f6fac1b8d907691b9d8b18eccab417f51e362102f00" }, +] + [[package]] name = "nvidia-cuda-cupti-cu12" version = "12.8.90" @@ -1478,6 +1660,16 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/41/bc/83f5426095d93694ae39fe1311431b5d5a9bb82e48bf0dd8e19be2765942/nvidia_cuda_cupti_cu12-12.8.90-py3-none-win_amd64.whl", hash = "sha256:bb479dcdf7e6d4f8b0b01b115260399bf34154a1a2e9fe11c85c517d87efd98e" }, ] +[[package]] +name = "nvidia-cuda-nvrtc" +version = "13.0.88" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/c3/68/483a78f5e8f31b08fb1bb671559968c0ca3a065ac7acabfc7cee55214fd6/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:ad9b6d2ead2435f11cbb6868809d2adeeee302e9bb94bcf0539c7a40d80e8575" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/b7/dc/6bb80850e0b7edd6588d560758f17e0550893a1feaf436807d64d2da040f/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d27f20a0ca67a4bb34268a5e951033496c5b74870b868bacd046b1b8e0c3267b" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/4a/af/345fedb9f4c76c84ab4fa445b36bd4048a4d9db60e6bc76b4f913ff4b852/nvidia_cuda_nvrtc-13.0.88-py3-none-win_amd64.whl", hash = "sha256:6bcd4e7f8e205cbe644f5a98f2f799bef9556fefc89dd786e79a16312ce49872" }, +] + [[package]] name = "nvidia-cuda-nvrtc-cu12" version = "12.8.93" @@ -1488,6 +1680,16 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/45/51/52a3d84baa2136cc8df15500ad731d74d3a1114d4c123e043cb608d4a32b/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-win_amd64.whl", hash = "sha256:7a4b6b2904850fe78e0bd179c4b655c404d4bb799ef03ddc60804247099ae909" }, ] +[[package]] +name = "nvidia-cuda-runtime" +version = "13.0.96" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/87/4f/17d7b9b8e285199c58ce28e31b5c5bbaa4d8271af06a89b6405258245de2/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ef9bcbe90493a2b9d810e43d249adb3d02e98dd30200d86607d8d02687c43f55" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/2e/24/d1558f3b68b1d26e706813b1d10aa1d785e4698c425af8db8edc3dced472/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f82250d7782aa23b6cfe765ecc7db554bd3c2870c43f3d1821f1d18aebf0548" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/b7/94/6b867483bec07da24ffa32736c79fabb94ef3a7af4d787a9d4a974868576/nvidia_cuda_runtime-13.0.96-py3-none-win_amd64.whl", hash = "sha256:f79298c8a098cec150a597c8eba58ecdab96e3bdc4b9bc4f9983635031740492" }, +] + [[package]] name = "nvidia-cuda-runtime-cu12" version = "12.8.90" @@ -1511,6 +1713,32 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/a7/a5/48f07449fc9c6cc146dcafe6149fa5d69630137d2ec5b7d9e09f255fadd7/nvidia_cudnn_cu12-9.19.0.56-py3-none-win_amd64.whl", hash = "sha256:cec70596b9ce878fab83810c3f5a2e606d35f510e5fee579759e4cbc68a23750" }, ] +[[package]] +name = "nvidia-cudnn-cu13" +version = "9.19.0.56" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +dependencies = [ + { name = "nvidia-cublas", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, +] +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/f1/84/26025437c1e6b61a707442184fa0c03d083b661adf3a3eecfd6d21677740/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:6ed29ffaee1176c612daf442e4dd6cfeb6a0caa43ddcbeb59da94953030b1be4" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/a3/22/0b4b932655d17a6da1b92fa92ab12844b053bb2ac2475e179ba6f043da1e/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:d20e1734305e9d68889a96e3f35094d733ff1f83932ebe462753973e53a572bf" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/91/a2/f020386683ee9ab2c9a9f7f79290d9b0d07f7241de54dc746af2abd188d2/nvidia_cudnn_cu13-9.19.0.56-py3-none-win_amd64.whl", hash = "sha256:40d8c375005bcb01495f8edf375230b203a411a0c05fb6dc92a3781edcb23eac" }, +] + +[[package]] +name = "nvidia-cufft" +version = "12.0.0.61" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +dependencies = [ + { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, +] +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/a8/2f/7b57e29836ea8714f81e9898409196f47d772d5ddedddf1592eadb8ab743/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6c44f692dce8fd5ffd3e3df134b6cdb9c2f72d99cf40b62c32dde45eea9ddad3" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/85/b2/f8af21a2ed1beed337a6a02c5a28aeb85441f4d578ec3d529543c775ea4b/nvidia_cufft-12.0.0.61-py3-none-win_amd64.whl", hash = "sha256:2abce5b39d2f5ae12730fb7e5db6696533e36c26e2d3e8fd1750bdd2853364eb" }, +] + [[package]] name = "nvidia-cufft-cu12" version = "11.3.3.83" @@ -1524,6 +1752,15 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/7d/ec/ce1629f1e478bb5ccd208986b5f9e0316a78538dd6ab1d0484f012f8e2a1/nvidia_cufft_cu12-11.3.3.83-py3-none-win_amd64.whl", hash = "sha256:7a64a98ef2a7c47f905aaf8931b69a3a43f27c55530c698bb2ed7c75c0b42cb7" }, ] +[[package]] +name = "nvidia-cufile" +version = "1.15.1.6" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/3f/70/4f193de89a48b71714e74602ee14d04e4019ad36a5a9f20c425776e72cd6/nvidia_cufile-1.15.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08a3ecefae5a01c7f5117351c64f17c7c62efa5fffdbe24fc7d298da19cd0b44" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/ab/73/cc4a14c9813a8a0d509417cf5f4bdaba76e924d58beb9864f5a7baceefbf/nvidia_cufile-1.15.1.6-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:bdc0deedc61f548bddf7733bdc216456c2fdb101d020e1ab4b88d232d5e2f6d1" }, +] + [[package]] name = "nvidia-cufile-cu12" version = "1.13.1.3" @@ -1533,6 +1770,16 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/1e/f5/5607710447a6fe9fd9b3283956fceeee8a06cda1d2f56ce31371f595db2a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:4beb6d4cce47c1a0f1013d72e02b0994730359e17801d395bdcbf20cfb3bb00a" }, ] +[[package]] +name = "nvidia-curand" +version = "10.4.0.35" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/1e/72/7c2ae24fb6b63a32e6ae5d241cc65263ea18d08802aaae087d9f013335a2/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:133df5a7509c3e292aaa2b477afd0194f06ce4ea24d714d616ff36439cee349a" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/a5/9f/be0a41ca4a4917abf5cb9ae0daff1a6060cc5de950aec0396de9f3b52bc5/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:1aee33a5da6e1db083fe2b90082def8915f30f3248d5896bcec36a579d941bfc" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/99/27/72103153b1ffc00e09fdc40ac970235343dcd1ea8bd762e84d2d73219ffa/nvidia_curand-10.4.0.35-py3-none-win_amd64.whl", hash = "sha256:65b1710aa6961d326b411e314b374290904c5ddf41dc3f766ebc3f1d7d4ca69f" }, +] + [[package]] name = "nvidia-curand-cu12" version = "10.3.9.90" @@ -1543,6 +1790,21 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/b9/75/70c05b2f3ed5be3bb30b7102b6eb78e100da4bbf6944fd6725c012831cab/nvidia_curand_cu12-10.3.9.90-py3-none-win_amd64.whl", hash = "sha256:f149a8ca457277da854f89cf282d6ef43176861926c7ac85b2a0fbd237c587ec" }, ] +[[package]] +name = "nvidia-cusolver" +version = "12.0.4.66" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +dependencies = [ + { name = "nvidia-cublas", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "nvidia-cusparse", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, +] +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/5f/67/cba3777620cdacb99102da4042883709c41c709f4b6323c10781a9c3aa34/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0a759da5dea5c0ea10fd307de75cdeb59e7ea4fcb8add0924859b944babf1112" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/99/ef/332a0101260ca78a1daef046bf0b06199e8ed4dac1d2aa698289c358169c/nvidia_cusolver-12.0.4.66-py3-none-win_amd64.whl", hash = "sha256:16515bd33a8e76bb54d024cfa068fa68d30e80fc34b9e1090813ea9362e0cb65" }, +] + [[package]] name = "nvidia-cusolver-cu12" version = "11.7.3.90" @@ -1558,6 +1820,19 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/13/c0/76ca8551b8a84146ffa189fec81c26d04adba4bc0dbe09cd6e6fd9b7de04/nvidia_cusolver_cu12-11.7.3.90-py3-none-win_amd64.whl", hash = "sha256:4a550db115fcabc4d495eb7d39ac8b58d4ab5d8e63274d3754df1c0ad6a22d34" }, ] +[[package]] +name = "nvidia-cusparse" +version = "12.6.3.3" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +dependencies = [ + { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, +] +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/fa/18/623c77619c31d62efd55302939756966f3ecc8d724a14dab2b75f1508850/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b3c89c88d01ee0e477cb7f82ef60a11a4bcd57b6b87c33f789350b59759360b" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/02/b0/b043d6f3480f102f885cf87fc3ffd3edcb5e23b855025a50e2ef4d059185/nvidia_cusparse-12.6.3.3-py3-none-win_amd64.whl", hash = "sha256:cbcf42feb737bd7ec15b4c0a63e62351886bd3f975027b8815d7f720a2b5ea79" }, +] + [[package]] name = "nvidia-cusparse-cu12" version = "12.5.8.93" @@ -1581,6 +1856,16 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/2f/d8/a6b0d0d0c2435e9310f3e2bb0d9c9dd4c33daef86aa5f30b3681defd37ea/nvidia_cusparselt_cu12-0.7.1-py3-none-win_amd64.whl", hash = "sha256:f67fbb5831940ec829c9117b7f33807db9f9678dc2a617fbe781cac17b4e1075" }, ] +[[package]] +name = "nvidia-cusparselt-cu13" +version = "0.8.0" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/46/10/8dcd1175260706a2fc92a16a52e306b71d4c1ea0b0cc4a9484183399818a/nvidia_cusparselt_cu13-0.8.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:400c6ed1cf6780fc6efedd64ec9f1345871767e6a1a0a552a1ea0578117ea77c" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/fd/53/43b0d71f4e702fa9733f8b4571fdca50a8813f1e450b656c239beff12315/nvidia_cusparselt_cu13-0.8.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:25e30a8a7323935d4ad0340b95a0b69926eee755767e8e0b1cf8dd85b197d3fd" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/57/de/8f0578928b9b1246d7b1324db0528e6b9f9fb54496a49f40bf71f09f1a27/nvidia_cusparselt_cu13-0.8.0-py3-none-win_amd64.whl", hash = "sha256:e80212ed7b1afc97102fbb2b5c82487aa73f6a0edfa6d26c5a152593e520bb8f" }, +] + [[package]] name = "nvidia-nccl-cu12" version = "2.28.9" @@ -1590,6 +1875,25 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/4a/4e/44dbb46b3d1b0ec61afda8e84837870f2f9ace33c564317d59b70bc19d3e/nvidia_nccl_cu12-2.28.9-py3-none-manylinux_2_18_x86_64.whl", hash = "sha256:485776daa8447da5da39681af455aa3b2c2586ddcf4af8772495e7c532c7e5ab" }, ] +[[package]] +name = "nvidia-nccl-cu13" +version = "2.28.9" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/39/55/1920646a2e43ffd4fc958536b276197ed740e9e0c54105b4bb3521591fc7/nvidia_nccl_cu13-2.28.9-py3-none-manylinux_2_18_aarch64.whl", hash = "sha256:01c873ba1626b54caa12272ed228dc5b2781545e0ae8ba3f432a8ef1c6d78643" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/b0/b4/878fefaad5b2bcc6fcf8d474a25e3e3774bc5133e4b58adff4d0bca238bc/nvidia_nccl_cu13-2.28.9-py3-none-manylinux_2_18_x86_64.whl", hash = "sha256:e4553a30f34195f3fa1da02a6da3d6337d28f2003943aa0a3d247bbc25fefc42" }, +] + +[[package]] +name = "nvidia-nvjitlink" +version = "13.0.88" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/56/7a/123e033aaff487c77107195fa5a2b8686795ca537935a24efae476c41f05/nvidia_nvjitlink-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:13a74f429e23b921c1109976abefacc69835f2f433ebd323d3946e11d804e47b" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/ab/2c/93c5250e64df4f894f1cbb397c6fd71f79813f9fd79d7cd61de3f97b3c2d/nvidia_nvjitlink-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e931536ccc7d467a98ba1d8b89ff7fa7f1fa3b13f2b0069118cd7f47bff07d0c" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/e4/01/07530b0e37546231052e30234540289c42eaffa486f1a34a87fed340157b/nvidia_nvjitlink-13.0.88-py3-none-win_amd64.whl", hash = "sha256:634e96e3da9ef845ae744097a1f289238ecf946ce0b82e93cdce14b9782e682f" }, +] + [[package]] name = "nvidia-nvjitlink-cu12" version = "12.8.93" @@ -1609,6 +1913,25 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/b5/09/6ea3ea725f82e1e76684f0708bbedd871fc96da89945adeba65c3835a64c/nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:042f2500f24c021db8a06c5eec2539027d57460e1c1a762055a6554f72c369bd" }, ] +[[package]] +name = "nvidia-nvshmem-cu13" +version = "3.4.5" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/dc/0f/05cc9c720236dcd2db9c1ab97fff629e96821be2e63103569da0c9b72f19/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dc2a197f38e5d0376ad52cd1a2a3617d3cdc150fd5966f4aee9bcebb1d68fe9" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/3c/35/a9bf80a609e74e3b000fef598933235c908fcefcef9026042b8e6dfde2a9/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:290f0a2ee94c9f3687a02502f3b9299a9f9fe826e6d0287ee18482e78d495b80" }, +] + +[[package]] +name = "nvidia-nvtx" +version = "13.0.85" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/c2/f3/d86c845465a2723ad7e1e5c36dcd75ddb82898b3f53be47ebd429fb2fa5d/nvidia_nvtx-13.0.85-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4936d1d6780fbe68db454f5e72a42ff64d1fd6397df9f363ae786930fd5c1cd4" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/a8/64/3708a90d1ebe202ffdeb7185f878a3c84d15c2b2c31858da2ce0583e2def/nvidia_nvtx-13.0.85-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb7780edb6b14107373c835bf8b72e7a178bac7367e23da7acb108f973f157a6" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/d2/50/0e2220f8620a177de994211186ffc5bfa9f2ce1e1282797f8f90096f9f88/nvidia_nvtx-13.0.85-py3-none-win_amd64.whl", hash = "sha256:d66ea44254dd3c6eacc300047af6e1288d2269dd072b417e0adffbf479e18519" }, +] + [[package]] name = "nvidia-nvtx-cu12" version = "12.8.90" @@ -1638,8 +1961,8 @@ version = "1.21.0" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ { name = "ml-dtypes" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "protobuf" }, { name = "typing-extensions" }, ] @@ -1680,8 +2003,8 @@ version = "0.2.1" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ { name = "ml-dtypes" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "onnx" }, { name = "sympy" }, { name = "typing-extensions" }, @@ -1696,21 +2019,21 @@ name = "onnxruntime" version = "1.24.3" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "flatbuffers", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "packaging", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "protobuf", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "sympy", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "flatbuffers", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "packaging", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "protobuf", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "sympy", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/15/41/3253db975a90c3ce1d475e2a230773a21cd7998537f0657947df6fb79861/onnxruntime-1.24.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3e6456801c66b095c5cd68e690ca25db970ea5202bd0c5b84a2c3ef7731c5a3c" }, @@ -1744,62 +2067,62 @@ name = "onnxruntime" version = "1.25.0" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", @@ -1826,10 +2149,10 @@ resolution-markers = [ "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "flatbuffers", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "packaging", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "protobuf", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "flatbuffers", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "packaging", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "protobuf", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/66/67/edb2cb6c4a38ebf539c3529c5449d68b8031f3006d8ee36574a6d45559b2/onnxruntime-1.25.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a71baa8e0e2f3417106e3a8b2183fd5741875b998041f1a2422a1d0240f302cb" }, @@ -1864,8 +2187,8 @@ version = "0.7.0" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ { name = "ml-dtypes" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "onnx" }, { name = "onnx-ir" }, { name = "packaging" }, @@ -1881,8 +2204,8 @@ name = "opencv-python" version = "4.13.0.92" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/fc/6f/5a28fef4c4a382be06afe3938c64cc168223016fa520c5abaf37e8862aa5/opencv_python-4.13.0.92-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:caf60c071ec391ba51ed00a4a920f996d0b64e3e46068aac1f646b5de0326a19" }, @@ -1909,20 +2232,20 @@ name = "pandas" version = "2.3.3" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "python-dateutil", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "pytz", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "tzdata", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "python-dateutil", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "pytz", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "tzdata", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b" } wheels = [ @@ -1980,62 +2303,62 @@ name = "pandas" version = "3.0.2" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", @@ -2062,9 +2385,9 @@ resolution-markers = [ "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "python-dateutil", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "tzdata", marker = "(python_full_version >= '3.11' and sys_platform == 'emscripten') or (python_full_version >= '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "python-dateutil", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "tzdata", marker = "(python_full_version >= '3.11' and sys_platform == 'emscripten') or (python_full_version >= '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/da/99/b342345300f13440fe9fe385c3c481e2d9a595ee3bab4d3219247ac94e9a/pandas-3.0.2.tar.gz", hash = "sha256:f4753e73e34c8d83221ba58f232433fca2748be8b18dbca02d242ed153945043" } wheels = [ @@ -2117,6 +2440,123 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/cb/2b/f8434233fab2bd66a02ec014febe4e5adced20e2693e0e90a07d118ed30e/pandas-3.0.2-cp314-cp314t-win_arm64.whl", hash = "sha256:5371b72c2d4d415d08765f32d689217a43227484e81b2305b52076e328f6f482" }, ] +[[package]] +name = "pandas-stubs" +version = "2.3.3.260113" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +resolution-markers = [ + "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", +] +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "types-pytz", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, +] +sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/92/5d/be23854a73fda69f1dbdda7bc10fbd6f930bd1fa87aaec389f00c901c1e8/pandas_stubs-2.3.3.260113.tar.gz", hash = "sha256:076e3724bcaa73de78932b012ec64b3010463d377fa63116f4e6850643d93800" } +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/d1/c6/df1fe324248424f77b89371116dab5243db7f052c32cc9fe7442ad9c5f75/pandas_stubs-2.3.3.260113-py3-none-any.whl", hash = "sha256:ec070b5c576e1badf12544ae50385872f0631fc35d99d00dc598c2954ec564d3" }, +] + +[[package]] +name = "pandas-stubs" +version = "3.0.0.260204" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +resolution-markers = [ + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", +] +dependencies = [ + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, +] +sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/27/1d/297ff2c7ea50a768a2247621d6451abb2a07c0e9be7ca6d36ebe371658e5/pandas_stubs-3.0.0.260204.tar.gz", hash = "sha256:bf9294b76352effcffa9cb85edf0bed1339a7ec0c30b8e1ac3d66b4228f1fbc3" } +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/7c/2f/f91e4eee21585ff548e83358332d5632ee49f6b2dcd96cb5dca4e0468951/pandas_stubs-3.0.0.260204-py3-none-any.whl", hash = "sha256:5ab9e4d55a6e2752e9720828564af40d48c4f709e6a2c69b743014a6fcb6c241" }, +] + [[package]] name = "pefile" version = "2024.8.26" @@ -2288,11 +2728,11 @@ version = "6.20.0" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ { name = "altgraph" }, - { name = "macholib", marker = "sys_platform == 'darwin' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "macholib", marker = "sys_platform == 'darwin' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "packaging" }, - { name = "pefile", marker = "sys_platform == 'win32' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "pefile", marker = "sys_platform == 'win32' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "pyinstaller-hooks-contrib" }, - { name = "pywin32-ctypes", marker = "sys_platform == 'win32' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "pywin32-ctypes", marker = "sys_platform == 'win32' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "setuptools" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/46/60/d03d52e6690d4e9caf333dcd14550cde634ce6c118b3bc8fa3112c3186fd/pyinstaller-6.20.0.tar.gz", hash = "sha256:95c5c7e03d5d61e9dfb8ef259c699cf492bb1041beb6dbe83696608cec07347a" } @@ -2516,8 +2956,8 @@ version = "3.8.1" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ { name = "colorlog" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "omegaconf" }, { name = "opencv-python" }, { name = "pillow" }, @@ -2552,20 +2992,20 @@ name = "scikit-learn" version = "1.7.2" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "joblib", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "threadpoolctl", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "joblib", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "threadpoolctl", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda" } wheels = [ @@ -2606,62 +3046,62 @@ name = "scikit-learn" version = "1.8.0" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", @@ -2688,10 +3128,10 @@ resolution-markers = [ "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "joblib", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "scipy", version = "1.17.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "threadpoolctl", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "joblib", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "scipy", version = "1.17.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "threadpoolctl", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd" } wheels = [ @@ -2738,17 +3178,17 @@ name = "scipy" version = "1.15.3" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf" } wheels = [ @@ -2804,62 +3244,62 @@ name = "scipy" version = "1.17.1" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", @@ -2886,7 +3326,7 @@ resolution-markers = [ "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0" } wheels = [ @@ -2966,8 +3406,8 @@ name = "shapely" version = "2.1.2" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/4d/bc/0989043118a27cccb4e906a46b7565ce36ca7b57f5a18b78f4f1b0f72d9d/shapely-2.1.2.tar.gz", hash = "sha256:2ed4ecb28320a433db18a5bf029986aa8afcfd740745e78847e330d5d94922a9" } wheels = [ @@ -3083,24 +3523,20 @@ version = "2.11.0" source = { registry = "https://download.pytorch.org/whl/cpu" } resolution-markers = [ "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin'", "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin'", "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin'", "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin'", "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin'", ] dependencies = [ - { name = "filelock", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (platform_machine != 's390x' and sys_platform == 'darwin')" }, - { name = "fsspec", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (platform_machine != 's390x' and sys_platform == 'darwin')" }, - { name = "jinja2", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (platform_machine != 's390x' and sys_platform == 'darwin')" }, - { name = "networkx", version = "3.4.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "networkx", version = "3.6.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "setuptools", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (platform_machine != 's390x' and sys_platform == 'darwin')" }, - { name = "sympy", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (platform_machine != 's390x' and sys_platform == 'darwin')" }, - { name = "typing-extensions", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (platform_machine != 's390x' and sys_platform == 'darwin')" }, + { name = "filelock", marker = "platform_machine != 's390x' and sys_platform == 'darwin'" }, + { name = "fsspec", marker = "platform_machine != 's390x' and sys_platform == 'darwin'" }, + { name = "jinja2", marker = "platform_machine != 's390x' and sys_platform == 'darwin'" }, + { name = "networkx", version = "3.4.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version >= '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "networkx", version = "3.6.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version < '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (python_full_version < '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "setuptools", marker = "platform_machine != 's390x' and sys_platform == 'darwin'" }, + { name = "sympy", marker = "platform_machine != 's390x' and sys_platform == 'darwin'" }, + { name = "typing-extensions", marker = "platform_machine != 's390x' and sys_platform == 'darwin'" }, ] wheels = [ { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91209c7d8a2460b76e8ff5b28b7623da4ab1d27474b79e1de83e954871985afe", upload-time = "2026-03-23T15:16:50Z" }, @@ -3123,6 +3559,7 @@ resolution-markers = [ "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin'", "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32'", "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32'", "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32'", @@ -3141,19 +3578,22 @@ resolution-markers = [ "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin'", "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin'", "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin'", "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin'", ] dependencies = [ - { name = "filelock", marker = "(python_full_version < '3.11' and platform_machine == 's390x') or sys_platform != 'darwin'" }, - { name = "fsspec", marker = "(python_full_version < '3.11' and platform_machine == 's390x') or sys_platform != 'darwin'" }, - { name = "jinja2", marker = "(python_full_version < '3.11' and platform_machine == 's390x') or sys_platform != 'darwin'" }, - { name = "networkx", version = "3.4.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "networkx", version = "3.6.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "setuptools", marker = "(python_full_version < '3.11' and platform_machine == 's390x') or sys_platform != 'darwin'" }, - { name = "sympy", marker = "(python_full_version < '3.11' and platform_machine == 's390x') or sys_platform != 'darwin'" }, - { name = "typing-extensions", marker = "(python_full_version < '3.11' and platform_machine == 's390x') or sys_platform != 'darwin'" }, + { name = "filelock", marker = "platform_machine == 's390x' or sys_platform != 'darwin'" }, + { name = "fsspec", marker = "platform_machine == 's390x' or sys_platform != 'darwin'" }, + { name = "jinja2", marker = "platform_machine == 's390x' or sys_platform != 'darwin'" }, + { name = "networkx", version = "3.4.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "networkx", version = "3.6.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "setuptools", marker = "platform_machine == 's390x' or sys_platform != 'darwin'" }, + { name = "sympy", marker = "platform_machine == 's390x' or sys_platform != 'darwin'" }, + { name = "typing-extensions", marker = "platform_machine == 's390x' or sys_platform != 'darwin'" }, ] wheels = [ { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.11.0%2Bcpu-cp310-cp310-linux_s390x.whl", hash = "sha256:3d8a7789e61dbf11f8922672c43354614b9b0debd40899c0a94f1ad9e0bd6bd9", upload-time = "2026-04-27T21:55:13Z" }, @@ -3219,13 +3659,13 @@ resolution-markers = [ "python_full_version < '3.11' and platform_machine == 's390x'", ] dependencies = [ - { name = "cuda-bindings", marker = "sys_platform == 'linux'" }, - { name = "cuda-toolkit", extra = ["cublas", "cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "(sys_platform == 'linux' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "cuda-bindings", version = "12.9.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "sys_platform == 'linux'" }, + { name = "cuda-toolkit", version = "12.8.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, extra = ["cublas", "cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "(sys_platform == 'linux' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "filelock" }, { name = "fsspec" }, { name = "jinja2" }, - { name = "networkx", version = "3.4.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "networkx", version = "3.6.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "networkx", version = "3.4.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "networkx", version = "3.6.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "nvidia-cudnn-cu12", marker = "sys_platform == 'linux'" }, { name = "nvidia-cusparselt-cu12", marker = "sys_platform == 'linux'" }, { name = "nvidia-nccl-cu12", marker = "sys_platform == 'linux'" }, @@ -3259,26 +3699,95 @@ wheels = [ { url = "https://download-r2.pytorch.org/whl/cu128/torch-2.11.0%2Bcu128-cp314-cp314t-win_amd64.whl", hash = "sha256:d86c125d720c2c368c53bd1a4ef062916d91fa965c10448c74c78b5d039faf2d", upload-time = "2026-04-27T18:01:14Z" }, ] +[[package]] +name = "torch" +version = "2.11.0+cu130" +source = { registry = "https://download.pytorch.org/whl/cu130" } +resolution-markers = [ + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.11' and platform_machine != 's390x'", + "python_full_version < '3.11' and platform_machine == 's390x'", +] +dependencies = [ + { name = "cuda-bindings", version = "13.2.0", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "sys_platform == 'linux'" }, + { name = "cuda-toolkit", version = "13.0.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, extra = ["cublas", "cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "(sys_platform == 'linux' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "filelock" }, + { name = "fsspec" }, + { name = "jinja2" }, + { name = "networkx", version = "3.4.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "networkx", version = "3.6.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "nvidia-cudnn-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nvshmem-cu13", marker = "sys_platform == 'linux'" }, + { name = "setuptools" }, + { name = "sympy" }, + { name = "triton", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:4af01fad0822353e766770ff2c7d6bdc2cbcc2ac7fcd6da93a9e3c6f3f932b21", upload-time = "2026-04-27T19:56:16Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:4c5be01584b7fee22d3c0d04062fd28026044acd07ffd0ee64cbd54b60e62d39", upload-time = "2026-04-27T19:56:40Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp310-cp310-win_amd64.whl", hash = "sha256:5b603a44f34816e18df254443a1fbfb4eef7d57128e5c7f6655f7fab45071f6e", upload-time = "2026-04-27T19:57:45Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:6304535e9e4cd1beeab449e407712602aa473a97e7b310dc5650ef50940bd94f", upload-time = "2026-04-27T19:58:56Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:225b22e0a4e36ea573d3a68796e6816a160616f67e8b8c55683a88bf7777f4cd", upload-time = "2026-04-27T19:59:21Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp311-cp311-win_amd64.whl", hash = "sha256:a1ff66c0ad21bf48c3187e84a08a6895d48e9bae435e27811b0b65f36bef4555", upload-time = "2026-04-27T20:00:28Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:252f237d417fac3ba59b1635815c1f035a8241f2af038f2c076ed430932d89f1", upload-time = "2026-04-27T20:01:46Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:96911323dcfcd42028c7e8edde7bdf25bb187753234e8775f0f3f112e86a22db", upload-time = "2026-04-27T20:02:14Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp312-cp312-win_amd64.whl", hash = "sha256:ef8beae16d781c3244ef28dc7bee6d8871c26bbde65d5bf66e902cb61972c4ab", upload-time = "2026-04-27T20:03:28Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:c3d60f79666b9101e3914a2e5dec2e81eac834e13cae0bcf59e94dc1a465f756", upload-time = "2026-04-27T20:04:49Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:554461b76f21211927c776056bcb0b00fb42972364794b686d768ebb0b586366", upload-time = "2026-04-27T20:05:21Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp313-cp313-win_amd64.whl", hash = "sha256:339801f2163698a53c7fb3c91883e7f44331d22c34d45acfbce4eff71f2332fa", upload-time = "2026-04-27T20:06:44Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:a33905bc3e093b25d2b019181cf834f7f7d4c562739e13dd36a798ecb2e411b0", upload-time = "2026-04-27T20:08:23Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:6fd10ed484eb695312ae829719888bb9f6c7f5e8503528e3e8ad1b98a45296c2", upload-time = "2026-04-27T20:08:56Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp313-cp313t-win_amd64.whl", hash = "sha256:21d2734fd02af45d19bb88c0ff2e86b238ce73f7bde6003ade7f1454ae299198", upload-time = "2026-04-27T20:10:20Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:efcdfe08ec2c9db28b50cc7329fed0c90bb74fa6fbce0f7eb12e20db2279a40f", upload-time = "2026-04-27T20:11:48Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:6ccc36928fd17c86011b46fb81bd2c85475f1fbf967dde758672d6a8d83a212a", upload-time = "2026-04-27T20:12:18Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp314-cp314-win_amd64.whl", hash = "sha256:d886f1c2f4406d7ad0c59f254ceb0a9c47a03e97a7c704b778a2066d752dde29", upload-time = "2026-04-27T20:13:41Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:bdb20f8b04e9fcaba2f354c3026667bebb74de8a92526b706aa735e2df334c24", upload-time = "2026-04-27T20:15:02Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:28f952cd4a927616ad9d77644a93237d1ca50bf30d0cf26962b9162d8a00ffa0", upload-time = "2026-04-27T20:15:30Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp314-cp314t-win_amd64.whl", hash = "sha256:d0a857adc487f275bfc9e7cdc51d12940613ba18b6362da214e20e9e3871f817", upload-time = "2026-04-27T20:16:48Z" }, +] + [[package]] name = "torchvision" version = "0.26.0" source = { registry = "https://download.pytorch.org/whl/cpu" } resolution-markers = [ "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin'", "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin'", "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin'", "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin'", "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "pillow", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (platform_machine != 's390x' and sys_platform == 'darwin')" }, - { name = "torch", version = "2.11.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (platform_machine != 's390x' and sys_platform == 'darwin')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version >= '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version < '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (python_full_version < '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "pillow", marker = "platform_machine != 's390x' and sys_platform == 'darwin'" }, + { name = "torch", version = "2.11.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "platform_machine != 's390x' and sys_platform == 'darwin'" }, ] wheels = [ { url = "https://download-r2.pytorch.org/whl/cpu/torchvision-0.26.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a06d4772a8e13e772906ed736cc53ec6639e5e60554f8e5fa6ca165aabebc464", upload-time = "2026-03-23T15:36:09Z" }, @@ -3301,6 +3810,7 @@ resolution-markers = [ "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin'", "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32'", "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32'", "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32'", @@ -3319,15 +3829,18 @@ resolution-markers = [ "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin'", "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin'", "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin'", "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "pillow", marker = "(python_full_version < '3.11' and platform_machine == 's390x') or sys_platform != 'darwin'" }, - { name = "torch", version = "2.11.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(python_full_version < '3.11' and platform_machine == 's390x') or sys_platform != 'darwin'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "pillow", marker = "platform_machine == 's390x' or sys_platform != 'darwin'" }, + { name = "torch", version = "2.11.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "platform_machine == 's390x' or sys_platform != 'darwin'" }, ] wheels = [ { url = "https://download-r2.pytorch.org/whl/cpu/torchvision-0.26.0%2Bcpu-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:97df9a8595dce256d2e6dd16bbcd1c68dd00eec712e37d4b6ec7985453ddc2aa", upload-time = "2026-03-23T15:36:09Z" }, @@ -3386,8 +3899,8 @@ resolution-markers = [ "python_full_version < '3.11' and platform_machine == 's390x'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "pillow" }, { name = "torch", version = "2.11.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" } }, ] @@ -3415,12 +3928,74 @@ wheels = [ { url = "https://download-r2.pytorch.org/whl/cu128/torchvision-0.26.0%2Bcu128-cp314-cp314t-win_amd64.whl", hash = "sha256:f160dc552a086244f7102c898f7be8ef46a41b36bce5ea80a4f2493cb30ca1fc", upload-time = "2026-04-09T23:21:41Z" }, ] +[[package]] +name = "torchvision" +version = "0.26.0+cu130" +source = { registry = "https://download.pytorch.org/whl/cu130" } +resolution-markers = [ + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.11' and platform_machine != 's390x'", + "python_full_version < '3.11' and platform_machine == 's390x'", +] +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "pillow" }, + { name = "torch", version = "2.11.0+cu130", source = { registry = "https://download.pytorch.org/whl/cu130" } }, +] +wheels = [ + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:3094bed175eee817f9fc61d1c8bdecb1d807c25f49517fbfe60f15d24135fcde", upload-time = "2026-03-23T15:36:25Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:7d820708732d2467caf2ef16c3ad60c8ff402bd05bd246b108ee080f3fbfdc6e", upload-time = "2026-03-23T15:36:25Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp310-cp310-win_amd64.whl", hash = "sha256:a410cf52350ff4a5e0a71cedaf03bdec973c7a22421f36163d887a251acf51d7", upload-time = "2026-03-23T15:36:25Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:31f87cd00c09e071980d6a4ce218289a73302ad6a7ce0b3b62a74a4081fc339d", upload-time = "2026-03-23T15:36:25Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:3b53e3b611561e03ac261d06cb3f38782120ad9e0b4cd9f01549799097c713a6", upload-time = "2026-03-23T15:36:25Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp311-cp311-win_amd64.whl", hash = "sha256:d019b9d02433515d59ad403d8a6f521da7844c030d6c8003eeec39e15e59f9fa", upload-time = "2026-04-09T23:21:52Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:e2b39db78be674ee4ce7e921f54b70e5c281594c9267d981c061684ed38df936", upload-time = "2026-03-23T15:36:26Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:0f030a9bd8ada1a31b7111ea1589c1ecb5fa0884fee700a203e731b4cf378a98", upload-time = "2026-03-23T15:36:26Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp312-cp312-win_amd64.whl", hash = "sha256:a3578f7c8e8a2724306c68c56873a1675fa7ce45471e18235c720a2ed242fe44", upload-time = "2026-04-09T23:21:53Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:3af2c699719cc0e2518bf317664200e5a987fb75a25b9b3bf3817a4796ddd64f", upload-time = "2026-03-23T15:36:26Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:441a98bed4fff1d54b8450499e377e1a605bec31f2ecb1a38a340f95dcc83897", upload-time = "2026-03-23T15:36:26Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp313-cp313-win_amd64.whl", hash = "sha256:64de855465d6de60583e776889fad9412480f9f9e04fdd8d17ae96fa93864e9a", upload-time = "2026-04-09T23:21:54Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:c3ac485da79552b4f579c525c826f7a63288b0d1cafc1201b16e1148bfdea69a", upload-time = "2026-03-23T15:36:26Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:110659ff38cd1d2ca0ac6e6a0f2c842fcb5fe739dfe65ff7456a12b2c4dce775", upload-time = "2026-03-23T15:36:26Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp313-cp313t-win_amd64.whl", hash = "sha256:a7e19c3ab5c6d8e3c9f8c6d427f6b8862dfb8227ea4a758ea7a709951daf2f0d", upload-time = "2026-04-09T23:21:55Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:13fe3dee74a9ee31b551b10a8b4113d9bc5212bb0572a07af88b34a5d25d9701", upload-time = "2026-03-23T15:36:26Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:8d6a83a639d14f7e84f6b838a17e26f9ce41cdbe3dfe0c29ef74b32eb398ba28", upload-time = "2026-03-23T15:36:26Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp314-cp314-win_amd64.whl", hash = "sha256:ab671ffe837aff470baad6af97133ea5a49f8ea2383832550e510a63caf711e4", upload-time = "2026-04-09T23:21:56Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:cd89effa98de436ec22ccbbd278cdadc0fdec8eb81a396150f50b321c2230866", upload-time = "2026-03-23T15:36:26Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:aacac4d990ac794f3abeca66cc26affb42fbeba9789e5c351183665bab4902d2", upload-time = "2026-03-23T15:36:26Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp314-cp314t-win_amd64.whl", hash = "sha256:23b9666084c72d07fc715001880b648e6a410796d1925c10f054f1ee034f5cc7", upload-time = "2026-04-09T23:21:57Z" }, +] + [[package]] name = "tqdm" version = "4.67.3" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "colorama", marker = "sys_platform == 'win32' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb" } wheels = [ @@ -3448,6 +4023,24 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/f6/56/6113c23ff46c00aae423333eb58b3e60bdfe9179d542781955a5e1514cb3/triton-3.6.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46bd1c1af4b6704e554cad2eeb3b0a6513a980d470ccfa63189737340c7746a7" }, ] +[[package]] +name = "types-pytz" +version = "2026.1.1.20260408" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/f7/b7/33f5a4f29b1f285b99ff79a607751a7996194cbb98705e331dab7a2daa28/types_pytz-2026.1.1.20260408.tar.gz", hash = "sha256:89b6a34b9198ea2a4b98a9d15cbca987053f52a105fd44f7ce3789cae4349408" } +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/ae/90/12c059e6bb330a22d9cc97daf027ac7fb7f50fbf518e4d88185b4d39120e/types_pytz-2026.1.1.20260408-py3-none-any.whl", hash = "sha256:c7e4dec76221fb7d0c97b91ad8561d689bebe39b6bcb7b728387e7ffd8cde788" }, +] + +[[package]] +name = "types-toml" +version = "0.10.8.20260408" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/71/9b/887564a51a84c96ba08b715570e546f0ea793df6372b736bfbc596ca5536/types_toml-0.10.8.20260408.tar.gz", hash = "sha256:6b30b031235565a12febb1388900b129f1adeabfcfa594da46d0372b2ac107ad" } +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/56/f1/942d95ba026779bc6e3064f8b094216588dc3276cc328cf8e03a0541918d/types_toml-0.10.8.20260408-py3-none-any.whl", hash = "sha256:e958d4c660385e548705a298f17dc162baf44c8b6d6aff79aeefe75f4f77ac87" }, +] + [[package]] name = "typing-extensions" version = "4.15.0" @@ -3480,8 +4073,8 @@ name = "windows-capture" version = "2.0.0" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "opencv-python" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/7e/60/1acdf4a4bb625bf04e94afde8bdb60a06997e644c6be7befe413dc5215ed/windows_capture-2.0.0.tar.gz", hash = "sha256:f1d587c182afa06c060e36ba2bd4f216a8ed03f320f51d7b6d5c8a2759469956" } diff --git a/maafw/DirectML.dll b/vendor/bin/maafw/DirectML.dll similarity index 100% rename from maafw/DirectML.dll rename to vendor/bin/maafw/DirectML.dll diff --git a/maafw/MaaAdbControlUnit.dll b/vendor/bin/maafw/MaaAdbControlUnit.dll similarity index 100% rename from maafw/MaaAdbControlUnit.dll rename to vendor/bin/maafw/MaaAdbControlUnit.dll diff --git a/maafw/MaaAgentClient.dll b/vendor/bin/maafw/MaaAgentClient.dll similarity index 100% rename from maafw/MaaAgentClient.dll rename to vendor/bin/maafw/MaaAgentClient.dll diff --git a/maafw/MaaAgentServer.dll b/vendor/bin/maafw/MaaAgentServer.dll similarity index 100% rename from maafw/MaaAgentServer.dll rename to vendor/bin/maafw/MaaAgentServer.dll diff --git a/maafw/MaaCustomControlUnit.dll b/vendor/bin/maafw/MaaCustomControlUnit.dll similarity index 100% rename from maafw/MaaCustomControlUnit.dll rename to vendor/bin/maafw/MaaCustomControlUnit.dll diff --git a/maafw/MaaFramework.dll b/vendor/bin/maafw/MaaFramework.dll similarity index 100% rename from maafw/MaaFramework.dll rename to vendor/bin/maafw/MaaFramework.dll diff --git a/maafw/MaaGamepadControlUnit.dll b/vendor/bin/maafw/MaaGamepadControlUnit.dll similarity index 100% rename from maafw/MaaGamepadControlUnit.dll rename to vendor/bin/maafw/MaaGamepadControlUnit.dll diff --git a/maafw/MaaNode.node b/vendor/bin/maafw/MaaNode.node similarity index 100% rename from maafw/MaaNode.node rename to vendor/bin/maafw/MaaNode.node diff --git a/maafw/MaaNodeServer.node b/vendor/bin/maafw/MaaNodeServer.node similarity index 100% rename from maafw/MaaNodeServer.node rename to vendor/bin/maafw/MaaNodeServer.node diff --git a/maafw/MaaPiCli.exe b/vendor/bin/maafw/MaaPiCli.exe similarity index 100% rename from maafw/MaaPiCli.exe rename to vendor/bin/maafw/MaaPiCli.exe diff --git a/maafw/MaaRecordControlUnit.dll b/vendor/bin/maafw/MaaRecordControlUnit.dll similarity index 100% rename from maafw/MaaRecordControlUnit.dll rename to vendor/bin/maafw/MaaRecordControlUnit.dll diff --git a/maafw/MaaReplayControlUnit.dll b/vendor/bin/maafw/MaaReplayControlUnit.dll similarity index 100% rename from maafw/MaaReplayControlUnit.dll rename to vendor/bin/maafw/MaaReplayControlUnit.dll diff --git a/maafw/MaaToolkit.dll b/vendor/bin/maafw/MaaToolkit.dll similarity index 100% rename from maafw/MaaToolkit.dll rename to vendor/bin/maafw/MaaToolkit.dll diff --git a/maafw/MaaUtils.dll b/vendor/bin/maafw/MaaUtils.dll similarity index 100% rename from maafw/MaaUtils.dll rename to vendor/bin/maafw/MaaUtils.dll diff --git a/maafw/MaaWin32ControlUnit.dll b/vendor/bin/maafw/MaaWin32ControlUnit.dll similarity index 100% rename from maafw/MaaWin32ControlUnit.dll rename to vendor/bin/maafw/MaaWin32ControlUnit.dll diff --git a/maafw/ViGEmClient.dll b/vendor/bin/maafw/ViGEmClient.dll similarity index 100% rename from maafw/ViGEmClient.dll rename to vendor/bin/maafw/ViGEmClient.dll diff --git a/maafw/fastdeploy_ppocr_maa.dll b/vendor/bin/maafw/fastdeploy_ppocr_maa.dll similarity index 100% rename from maafw/fastdeploy_ppocr_maa.dll rename to vendor/bin/maafw/fastdeploy_ppocr_maa.dll diff --git a/maafw/onnxruntime_maa.dll b/vendor/bin/maafw/onnxruntime_maa.dll similarity index 100% rename from maafw/onnxruntime_maa.dll rename to vendor/bin/maafw/onnxruntime_maa.dll diff --git a/maafw/opencv_world4_maa.dll b/vendor/bin/maafw/opencv_world4_maa.dll similarity index 100% rename from maafw/opencv_world4_maa.dll rename to vendor/bin/maafw/opencv_world4_maa.dll diff --git a/maafw/plugins/MaaPluginDemo.dll b/vendor/bin/maafw/plugins/MaaPluginDemo.dll similarity index 100% rename from maafw/plugins/MaaPluginDemo.dll rename to vendor/bin/maafw/plugins/MaaPluginDemo.dll diff --git a/platform-tools/AdbWinApi.dll b/vendor/bin/platform-tools/AdbWinApi.dll similarity index 100% rename from platform-tools/AdbWinApi.dll rename to vendor/bin/platform-tools/AdbWinApi.dll diff --git a/platform-tools/AdbWinUsbApi.dll b/vendor/bin/platform-tools/AdbWinUsbApi.dll similarity index 100% rename from platform-tools/AdbWinUsbApi.dll rename to vendor/bin/platform-tools/AdbWinUsbApi.dll diff --git a/platform-tools/NOTICE.txt b/vendor/bin/platform-tools/NOTICE.txt similarity index 100% rename from platform-tools/NOTICE.txt rename to vendor/bin/platform-tools/NOTICE.txt diff --git a/platform-tools/adb.exe b/vendor/bin/platform-tools/adb.exe similarity index 100% rename from platform-tools/adb.exe rename to vendor/bin/platform-tools/adb.exe diff --git a/platform-tools/etc1tool.exe b/vendor/bin/platform-tools/etc1tool.exe similarity index 100% rename from platform-tools/etc1tool.exe rename to vendor/bin/platform-tools/etc1tool.exe diff --git a/platform-tools/fastboot.exe b/vendor/bin/platform-tools/fastboot.exe similarity index 100% rename from platform-tools/fastboot.exe rename to vendor/bin/platform-tools/fastboot.exe diff --git a/platform-tools/hprof-conv.exe b/vendor/bin/platform-tools/hprof-conv.exe similarity index 100% rename from platform-tools/hprof-conv.exe rename to vendor/bin/platform-tools/hprof-conv.exe diff --git a/platform-tools/libwinpthread-1.dll b/vendor/bin/platform-tools/libwinpthread-1.dll similarity index 100% rename from platform-tools/libwinpthread-1.dll rename to vendor/bin/platform-tools/libwinpthread-1.dll diff --git a/platform-tools/make_f2fs.exe b/vendor/bin/platform-tools/make_f2fs.exe similarity index 100% rename from platform-tools/make_f2fs.exe rename to vendor/bin/platform-tools/make_f2fs.exe diff --git a/platform-tools/make_f2fs_casefold.exe b/vendor/bin/platform-tools/make_f2fs_casefold.exe similarity index 100% rename from platform-tools/make_f2fs_casefold.exe rename to vendor/bin/platform-tools/make_f2fs_casefold.exe diff --git a/platform-tools/mke2fs.conf b/vendor/bin/platform-tools/mke2fs.conf similarity index 100% rename from platform-tools/mke2fs.conf rename to vendor/bin/platform-tools/mke2fs.conf diff --git a/platform-tools/mke2fs.exe b/vendor/bin/platform-tools/mke2fs.exe similarity index 100% rename from platform-tools/mke2fs.exe rename to vendor/bin/platform-tools/mke2fs.exe diff --git a/platform-tools/source.properties b/vendor/bin/platform-tools/source.properties similarity index 100% rename from platform-tools/source.properties rename to vendor/bin/platform-tools/source.properties diff --git a/platform-tools/sqlite3.exe b/vendor/bin/platform-tools/sqlite3.exe similarity index 100% rename from platform-tools/sqlite3.exe rename to vendor/bin/platform-tools/sqlite3.exe From 734603ea00d84915dc9a67796ae27786699fba69 Mon Sep 17 00:00:00 2001 From: 1ZUMIKun <68433151+1ZUMIKun@users.noreply.github.com> Date: Fri, 1 May 2026 11:47:25 +0800 Subject: [PATCH 5/9] =?UTF-8?q?Revert=20"Refactor=20project=20structure=20?= =?UTF-8?q?and=20enhance=20launcher=20script=20functionality=20=E2=80=A6"?= =?UTF-8?q?=20(#152)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 8c1a922659ba5d3602d825a44add22e6aec4c7a1. --- .github/workflows/build-and-release.yml | 4 +- .gitignore | 19 +- WinningRate_Statistics.py | 942 +++++++++ src/game/auto_fetch.py => auto_fetch.py | 427 ++-- src/core/config.py => config.py | 31 +- src/core/constants.py => constants.py | 58 +- ...ode_style_fix.py => dark_mode_style_fix.py | 0 src/data/data_package.py => data_package.py | 4 +- ...50\346\255\244\350\267\257\345\276\204.md" | 0 ...60\346\215\256\345\220\210\345\271\266.py" | 107 +- ...eld_recognition.py => field_recognition.py | 137 +- ...nd_monster_zone.py => find_monster_zone.py | 367 +--- .../assets/icons => ico}/background.png | Bin {src/resources/assets/icons => ico}/icon.ico | Bin .../assets/icons => ico}/icon_64x64.ico | Bin ...0\345\212\233\350\243\205\347\224\262.png" | Bin ...0\345\212\233\350\243\205\347\224\262.png" | Bin .../resources/assets/images => images}/eg.png | Bin .../assets/images => images}/empty.png | Bin .../login/announcement_close.png | Bin .../login/competition_page.png | Bin .../login/event_claim_close.png | Bin .../images => images}/login/login_button.png | Bin .../images => images}/login/login_off.png | Bin .../assets/images => images}/process/0.png | Bin .../assets/images => images}/process/1.png | Bin .../assets/images => images}/process/10.png | Bin .../assets/images => images}/process/11.png | Bin .../assets/images => images}/process/12.png | Bin .../assets/images => images}/process/13.png | Bin .../assets/images => images}/process/14.png | Bin .../assets/images => images}/process/15.png | Bin .../process/1746774840_28_5.png1s.png | Bin .../assets/images => images}/process/2.png | Bin .../assets/images => images}/process/3.png | Bin .../assets/images => images}/process/4.png | Bin .../assets/images => images}/process/5.png | Bin .../assets/images => images}/process/6.png | Bin .../assets/images => images}/process/7.png | Bin .../assets/images => images}/process/8.png | Bin .../assets/images => images}/process/9.png | Bin ...5\344\273\207\350\200\205\342\200\235.png" | Bin .../\342\200\234\345\246\222\342\200\235.png" | Bin ...4\345\267\242\347\251\264\342\200\235.png" | Bin ...4\345\272\236\350\264\235\342\200\235.png" | Bin ...5\347\237\263\346\234\272\342\200\235.png" | Bin ...4\347\233\233\346\200\222\342\200\235.png" | Bin ...4\350\207\252\345\234\250\342\200\235.png" | Bin ...4\346\204\217\345\277\227\342\200\235.png" | Bin ...3\347\224\237\351\243\216\342\200\235.png" | Bin .../\342\200\234\351\227\250\342\200\235.png" | Bin ...4\351\230\277\345\222\254\342\200\235.png" | Bin ...1\350\242\255\345\274\251\346\211\213.png" | Bin ...5\346\262\231\346\273\251\350\275\246.png" | Bin ...7\345\215\253\346\263\225\350\200\205.png" | Bin ...0\345\216\237\346\234\257\345\270\210.png" | Bin ...6\346\272\220\347\237\263\350\231\253.png" | Bin ...3\351\233\266\351\252\221\345\243\253.png" | Bin .../\345\207\266\350\261\225\345\205\275.png" | Bin ...5\347\224\262\346\255\245\345\205\265.png" | Bin ...2\345\267\250\345\262\251\350\233\233.png" | Bin ...3\345\233\242\345\255\246\345\276\222.png" | Bin ...7\345\207\277\347\237\263\350\200\205.png" | Bin .../\345\244\215\344\273\207\350\200\205.png" | Bin ...0\345\237\265\346\235\203\345\214\226.png" | Bin ...3\346\265\201\346\265\252\350\200\205.png" | Bin ...7\350\200\205\344\277\241\344\275\277.png" | Bin ...7\347\252\245\351\255\205\344\272\272.png" | Bin .../\345\262\201\347\233\270.png" | Bin .../\345\262\251\345\206\240\345\205\275.png" | Bin ...1\351\224\213\345\215\253\351\225\277.png" | Bin ...3\346\211\213\345\233\232\347\212\257.png" | Bin ...4\345\245\275\346\210\230\350\200\205.png" | Bin ...4\347\240\264\345\235\217\347\216\213.png" | Bin ...3\345\233\242\345\255\246\345\276\222.png" | Bin .../\346\227\272\350\264\242.png" | Bin ...0\351\243\237\344\272\272\350\212\261.png" | Bin ...0\351\243\237\350\231\253\350\212\261.png" | Bin ...2\346\216\250\350\215\220\350\200\205.png" | Bin ...1\345\273\211\345\247\206\346\226\257.png" | Bin ...0\346\261\275\346\210\230\350\275\246.png" | Bin .../\346\236\243\345\244\247\345\210\200.png" | Bin ...7\346\234\275\344\271\213\347\247\215.png" | Bin .../\346\243\230\345\205\275.png" | Bin ...0\345\233\242\351\274\223\346\211\213.png" | Bin ...0\345\205\213\346\226\257\346\211\213.png" | Bin ...4\346\211\213\351\207\215\350\211\207.png" | Bin ...4\351\201\201\345\277\215\350\200\205.png" | Bin .../\346\262\211\346\262\231.png" | Bin ...3\345\233\242\347\262\276\351\224\220.png" | Bin ...7\350\267\265\350\241\214\350\200\205.png" | Bin ...5\345\262\251\345\267\250\345\203\217.png" | Bin ...7\350\243\202\347\244\201\350\200\205.png" | Bin ...6\350\243\205\351\251\256\345\205\275.png" | Bin ...3\351\230\237\347\233\276\345\215\253.png" | Bin ...4\345\277\227\346\204\277\350\200\205.png" | Bin ...3\347\225\270\345\217\230\344\275\223.png" | Bin ...5\346\272\220\347\237\263\350\231\253.png" | Bin .../\347\202\255\351\225\277\347\237\233.png" | Bin ...6\345\207\273\347\273\204\351\225\277.png" | Bin ...0\346\272\220\347\237\263\350\231\253.png" | Bin ...7\344\270\273\347\273\204\351\225\277.png" | Bin ...47\232\204\347\214\216\347\213\227pro.png" | Bin ...1\345\207\273\346\255\245\345\205\265.png" | Bin .../\347\216\211\345\217\214\345\211\221.png" | Bin .../\347\224\260\351\274\267.png" | Bin ...0\350\265\230\347\224\237\347\211\251.png" | Bin .../\347\230\264.png" | Bin ...7\350\204\211\345\256\210\345\215\253.png" | Bin ...1\345\244\264\346\260\264\346\211\213.png" | Bin ...4\347\224\262\347\210\252\345\205\275.png" | Bin .../\347\251\272\351\231\215\345\205\265.png" | Bin ...0\346\233\262\345\220\210\345\243\260.png" | Bin ...0\350\264\245\351\252\221\345\243\253.png" | Bin ...3\345\220\211\344\273\226\342\200\235.png" | Bin ...4\350\264\235\346\226\257\342\200\235.png" | Bin ...7\345\212\253\346\216\240\350\200\205.png" | Bin ...1\345\244\247\345\211\221\346\211\213.png" | Bin ...2\347\231\276\345\244\253\351\225\277.png" | Bin ...5\345\220\236\345\231\254\350\200\205.png" | Bin ...7\346\234\275\345\256\277\345\215\253.png" | Bin ...7\346\234\275\346\210\230\350\275\246.png" | Bin ...0\351\233\206\345\205\273\350\200\205.png" | Bin ...6\344\272\272\345\274\251\346\211\213.png" | Bin ...2\346\211\260\344\271\261\350\200\205.png" | Bin ...7\347\232\204\345\267\250\345\203\217.png" | Bin ...7\237\263\350\231\253\302\267\316\261.png" | Bin ...5\351\230\262\345\276\241\350\200\205.png" | Bin ...0\345\207\273\344\272\272\346\211\213.png" | Bin ...7\346\234\257\345\270\210\342\200\235.png" | Bin ...0\345\241\224\346\234\257\345\270\210.png" | Bin ...0\346\231\256\345\260\274\345\205\213.png" | Bin ...6\350\243\205\344\272\272\345\221\230.png" | Bin ...5\346\272\220\347\237\263\350\231\253.png" | Bin ...4\346\272\220\347\237\263\350\231\253.png" | Bin src/ui/input_panel_ui.py => input_panel_ui.py | 163 +- src/data/load_data.py => loadData.py | 220 +- src/game/login.py => login.py | 318 +-- ...a_adb_connector.py => maa_adb_connector.py | 131 +- {vendor/bin/maafw => maafw}/DirectML.dll | Bin .../bin/maafw => maafw}/MaaAdbControlUnit.dll | Bin .../bin/maafw => maafw}/MaaAgentClient.dll | Bin .../bin/maafw => maafw}/MaaAgentServer.dll | Bin .../maafw => maafw}/MaaCustomControlUnit.dll | Bin {vendor/bin/maafw => maafw}/MaaFramework.dll | Bin .../maafw => maafw}/MaaGamepadControlUnit.dll | Bin {vendor/bin/maafw => maafw}/MaaNode.node | Bin .../bin/maafw => maafw}/MaaNodeServer.node | Bin {vendor/bin/maafw => maafw}/MaaPiCli.exe | Bin .../maafw => maafw}/MaaRecordControlUnit.dll | Bin .../maafw => maafw}/MaaReplayControlUnit.dll | Bin {vendor/bin/maafw => maafw}/MaaToolkit.dll | Bin {vendor/bin/maafw => maafw}/MaaUtils.dll | Bin .../maafw => maafw}/MaaWin32ControlUnit.dll | Bin {vendor/bin/maafw => maafw}/ViGEmClient.dll | Bin .../maafw => maafw}/fastdeploy_ppocr_maa.dll | Bin .../bin/maafw => maafw}/onnxruntime_maa.dll | Bin .../bin/maafw => maafw}/opencv_world4_maa.dll | Bin .../maafw => maafw}/plugins/MaaPluginDemo.dll | Bin main.py | 383 ++-- main.spec | 14 +- main_old.py | 650 ++++++ main_sim.py | 569 ++---- {src/analysis => models}/__init__.py | 0 {src/models => models}/model.py | 14 +- {src/models => models}/muon.py | 55 +- models/update.md | 7 + src/resources/data/monster.csv => monster.csv | 0 ...ter_greenvine.csv => monster_greenvine.csv | 0 multi_instance.py | 432 ++-- .../AdbWinApi.dll | Bin .../AdbWinUsbApi.dll | Bin .../NOTICE.txt | 0 .../platform-tools => platform-tools}/adb.exe | Bin .../etc1tool.exe | Bin .../fastboot.exe | Bin .../hprof-conv.exe | Bin .../libwinpthread-1.dll | Bin .../make_f2fs.exe | Bin .../make_f2fs_casefold.exe | Bin .../mke2fs.conf | 0 .../mke2fs.exe | Bin .../source.properties | 0 .../sqlite3.exe | Bin src/models/predict.py => predict.py | 189 +- src/models/predict_onnx.py => predict_onnx.py | 91 +- pyproject.toml | 4 +- src/recognition/recognize.py => recognize.py | 146 +- src/analysis/sim_mc.py => sim_mc.py | 330 +-- ...story_match.py => similar_history_match.py | 210 +- ...match_ui.py => simular_history_match_ui.py | 86 +- simulator/.gitignore | 1 + {src/simulation => simulator}/README.md | 0 {src/core => simulator}/__init__.py | 0 {src/simulation => simulator}/arknights.csv | 0 {src/simulation => simulator}/battle_field.py | 154 +- {src/simulation => simulator}/elemental.py | 50 +- {src/simulation => simulator}/monsters.json | 0 {src/simulation => simulator}/monsters.py | 623 ++---- {src/simulation => simulator}/projectiles.py | 138 +- simulator/scene.json | 58 + {src/simulation => simulator}/simulate.py | 74 +- simulator/stats.py | 172 ++ {src/simulation => simulator}/utils.py | 57 +- {src/simulation => simulator}/vector2d.py | 57 +- {src/simulation => simulator}/zone.py | 57 +- specialmonster.py | 37 + src/analysis/winning_rate_statistics.py | 1131 ----------- src/core/paths.py | 38 - src/data/__init__.py | 0 src/game/__init__.py | 0 src/models/__init__.py | 0 src/recognition/__init__.py | 0 src/recognition/specialmonster.py | 44 - src/resources/config/maa_option.json | 7 - src/simulation/__init__.py | 0 src/simulation/scene.json | 206 -- src/simulation/stats.py | 191 -- .../extract_webm_frames.py | 93 - src/tools/data_washer_new.py | 1383 ------------- src/ui/__init__.py | 0 src/utils/__init__.py | 0 {src/tools => tools}/HumanDataCheck.py | 240 +-- .../battlefield_composite/.gitignore | 0 .../battlefield_composite.py | 41 +- .../extract_webm_frames.py | 92 + .../monster_images/IM-1.png | Bin .../monster_images/battlefield_empty.jpg | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...4-\346\210\230\346\226\227-A_Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...4-\346\210\230\346\226\227-Move_A-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...4-\346\210\230\346\226\227-A_Move-x1.webm" | Bin ...\346\210\230\346\226\227-Move_red-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...-\346\210\230\346\226\227-C1_Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...4-\346\210\230\346\226\227-Attack-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...346\210\230\346\226\227-Move_Loop-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...346\210\230\346\226\227-Move_Loop-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...346\210\230\346\226\227-Move_Loop-x1.webm" | Bin ...346\210\230\346\226\227-Move_Loop-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin .../battlefield_recognize/class_to_idx.json | 0 ...7\346\256\265\350\257\264\346\230\216.txt" | 0 {src/tools => tools}/convert_model.py | 20 +- {src/tools => tools}/data_cleaning.py | 6 +- .../data_cleaning_with_field_recognize.py | 191 +- .../data_cleaning_with_field_recognize_gpu.py | 208 +- {src/tools => tools}/data_nanWriter.py | 7 +- tools/data_washer_new.py | 904 +++++++++ {src/tools => tools}/package.py | 17 +- src/models/train.py => train.py | 264 +-- src/utils/unit.py => unit.py | 2 +- uv.lock | 1801 ++++++----------- src/models/val.py => val.py | 55 +- src/game/winrt_capture.py => winrt_capture.py | 60 +- ...\351\200\211\346\213\251\347\211\210).bat" | 152 +- 299 files changed, 5697 insertions(+), 9042 deletions(-) create mode 100644 WinningRate_Statistics.py rename src/game/auto_fetch.py => auto_fetch.py (74%) rename src/core/config.py => config.py (54%) rename src/core/constants.py => constants.py (89%) rename src/ui/dark_mode_style_fix.py => dark_mode_style_fix.py (100%) rename src/data/data_package.py => data_package.py (96%) rename src/__init__.py => "data_train/package/\346\225\260\346\215\256\345\214\205\346\224\276\345\234\250\346\255\244\350\267\257\345\276\204.md" (100%) rename src/tools/merge_data.py => "data_train/\346\225\260\346\215\256\345\220\210\345\271\266.py" (69%) rename src/recognition/field_recognition.py => field_recognition.py (68%) rename src/recognition/find_monster_zone.py => find_monster_zone.py (58%) rename {src/resources/assets/icons => ico}/background.png (100%) rename {src/resources/assets/icons => ico}/icon.ico (100%) rename {src/resources/assets/icons => ico}/icon_64x64.ico (100%) rename "src/resources/assets/images/R-11\347\252\201\345\207\273\345\212\250\345\212\233\350\243\205\347\224\262.png" => "images/R-11\347\252\201\345\207\273\345\212\250\345\212\233\350\243\205\347\224\262.png" (100%) rename "src/resources/assets/images/R-31\351\207\215\345\236\213\345\212\250\345\212\233\350\243\205\347\224\262.png" => "images/R-31\351\207\215\345\236\213\345\212\250\345\212\233\350\243\205\347\224\262.png" (100%) rename {src/resources/assets/images => images}/eg.png (100%) rename {src/resources/assets/images => images}/empty.png (100%) rename {src/resources/assets/images => images}/login/announcement_close.png (100%) rename {src/resources/assets/images => images}/login/competition_page.png (100%) rename {src/resources/assets/images => images}/login/event_claim_close.png (100%) rename {src/resources/assets/images => images}/login/login_button.png (100%) rename {src/resources/assets/images => images}/login/login_off.png (100%) rename {src/resources/assets/images => images}/process/0.png (100%) rename {src/resources/assets/images => images}/process/1.png (100%) rename {src/resources/assets/images => images}/process/10.png (100%) rename {src/resources/assets/images => images}/process/11.png (100%) rename {src/resources/assets/images => images}/process/12.png (100%) rename {src/resources/assets/images => images}/process/13.png (100%) rename {src/resources/assets/images => images}/process/14.png (100%) rename {src/resources/assets/images => images}/process/15.png (100%) rename {src/resources/assets/images => images}/process/1746774840_28_5.png1s.png (100%) rename {src/resources/assets/images => images}/process/2.png (100%) rename {src/resources/assets/images => images}/process/3.png (100%) rename {src/resources/assets/images => images}/process/4.png (100%) rename {src/resources/assets/images => images}/process/5.png (100%) rename {src/resources/assets/images => images}/process/6.png (100%) rename {src/resources/assets/images => images}/process/7.png (100%) rename {src/resources/assets/images => images}/process/8.png (100%) rename {src/resources/assets/images => images}/process/9.png (100%) rename "src/resources/assets/images/\342\200\234\345\244\215\344\273\207\350\200\205\342\200\235.png" => "images/\342\200\234\345\244\215\344\273\207\350\200\205\342\200\235.png" (100%) rename "src/resources/assets/images/\342\200\234\345\246\222\342\200\235.png" => "images/\342\200\234\345\246\222\342\200\235.png" (100%) rename "src/resources/assets/images/\342\200\234\345\267\242\347\251\264\342\200\235.png" => "images/\342\200\234\345\267\242\347\251\264\342\200\235.png" (100%) rename "src/resources/assets/images/\342\200\234\345\272\236\350\264\235\342\200\235.png" => "images/\342\200\234\345\272\236\350\264\235\342\200\235.png" (100%) rename "src/resources/assets/images/\342\200\234\346\212\225\347\237\263\346\234\272\342\200\235.png" => "images/\342\200\234\346\212\225\347\237\263\346\234\272\342\200\235.png" (100%) rename "src/resources/assets/images/\342\200\234\347\233\233\346\200\222\342\200\235.png" => "images/\342\200\234\347\233\233\346\200\222\342\200\235.png" (100%) rename "src/resources/assets/images/\342\200\234\350\207\252\345\234\250\342\200\235.png" => "images/\342\200\234\350\207\252\345\234\250\342\200\235.png" (100%) rename "src/resources/assets/images/\342\200\234\350\220\250\347\261\263\347\232\204\346\204\217\345\277\227\342\200\235.png" => "images/\342\200\234\350\220\250\347\261\263\347\232\204\346\204\217\345\277\227\342\200\235.png" (100%) rename "src/resources/assets/images/\342\200\234\351\222\263\351\222\263\347\224\237\351\243\216\342\200\235.png" => "images/\342\200\234\351\222\263\351\222\263\347\224\237\351\243\216\342\200\235.png" (100%) rename "src/resources/assets/images/\342\200\234\351\227\250\342\200\235.png" => "images/\342\200\234\351\227\250\342\200\235.png" (100%) rename "src/resources/assets/images/\342\200\234\351\230\277\345\222\254\342\200\235.png" => "images/\342\200\234\351\230\277\345\222\254\342\200\235.png" (100%) rename "src/resources/assets/images/\344\271\214\350\220\250\346\226\257\347\252\201\350\242\255\345\274\251\346\211\213.png" => "images/\344\271\214\350\220\250\346\226\257\347\252\201\350\242\255\345\274\251\346\211\213.png" (100%) rename "src/resources/assets/images/\345\205\250\345\260\201\351\227\255\346\262\231\346\273\251\350\275\246.png" => "images/\345\205\250\345\260\201\351\227\255\346\262\231\346\273\251\350\275\246.png" (100%) rename "src/resources/assets/images/\345\206\260\345\216\237\345\215\253\346\263\225\350\200\205.png" => "images/\345\206\260\345\216\237\345\215\253\346\263\225\350\200\205.png" (100%) rename "src/resources/assets/images/\345\206\260\345\216\237\346\234\257\345\270\210.png" => "images/\345\206\260\345\216\237\346\234\257\345\270\210.png" (100%) rename "src/resources/assets/images/\345\206\260\347\210\206\346\272\220\347\237\263\350\231\253.png" => "images/\345\206\260\347\210\206\346\272\220\347\237\263\350\231\253.png" (100%) rename "src/resources/assets/images/\345\207\213\351\233\266\351\252\221\345\243\253.png" => "images/\345\207\213\351\233\266\351\252\221\345\243\253.png" (100%) rename "src/resources/assets/images/\345\207\266\350\261\225\345\205\275.png" => "images/\345\207\266\350\261\225\345\205\275.png" (100%) rename "src/resources/assets/images/\345\217\215\350\243\205\347\224\262\346\255\245\345\205\265.png" => "images/\345\217\215\350\243\205\347\224\262\346\255\245\345\205\265.png" (100%) rename "src/resources/assets/images/\345\217\230\345\274\202\345\267\250\345\262\251\350\233\233.png" => "images/\345\217\230\345\274\202\345\267\250\345\262\251\350\233\233.png" (100%) rename "src/resources/assets/images/\345\221\274\345\225\270\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222.png" => "images/\345\221\274\345\225\270\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222.png" (100%) rename "src/resources/assets/images/\345\233\272\346\265\267\345\207\277\347\237\263\350\200\205.png" => "images/\345\233\272\346\265\267\345\207\277\347\237\263\350\200\205.png" (100%) rename "src/resources/assets/images/\345\244\215\344\273\207\350\200\205.png" => "images/\345\244\215\344\273\207\350\200\205.png" (100%) rename "src/resources/assets/images/\345\245\216\351\232\206\357\274\214\346\221\251\350\257\203\350\220\250\345\237\265\346\235\203\345\214\226.png" => "images/\345\245\216\351\232\206\357\274\214\346\221\251\350\257\203\350\220\250\345\237\265\346\235\203\345\214\226.png" (100%) rename "src/resources/assets/images/\345\256\277\344\270\273\346\265\201\346\265\252\350\200\205.png" => "images/\345\256\277\344\270\273\346\265\201\346\265\252\350\200\205.png" (100%) rename "src/resources/assets/images/\345\257\273\350\267\257\350\200\205\344\277\241\344\275\277.png" => "images/\345\257\273\350\267\257\350\200\205\344\277\241\344\275\277.png" (100%) rename "src/resources/assets/images/\345\261\261\346\265\267\344\274\227\347\252\245\351\255\205\344\272\272.png" => "images/\345\261\261\346\265\267\344\274\227\347\252\245\351\255\205\344\272\272.png" (100%) rename "src/resources/assets/images/\345\262\201\347\233\270.png" => "images/\345\262\201\347\233\270.png" (100%) rename "src/resources/assets/images/\345\262\251\345\206\240\345\205\275.png" => "images/\345\262\251\345\206\240\345\205\275.png" (100%) rename "src/resources/assets/images/\345\274\247\345\205\211\351\224\213\345\215\253\351\225\277.png" => "images/\345\274\247\345\205\211\351\224\213\345\215\253\351\225\277.png" (100%) rename "src/resources/assets/images/\346\213\263\346\211\213\345\233\232\347\212\257.png" => "images/\346\213\263\346\211\213\345\233\232\347\212\257.png" (100%) rename "src/resources/assets/images/\346\217\220\344\272\232\345\215\241\344\271\214\345\245\275\346\210\230\350\200\205.png" => "images/\346\217\220\344\272\232\345\215\241\344\271\214\345\245\275\346\210\230\350\200\205.png" (100%) rename "src/resources/assets/images/\346\217\220\344\272\232\345\215\241\344\271\214\347\240\264\345\235\217\347\216\213.png" => "images/\346\217\220\344\272\232\345\215\241\344\271\214\347\240\264\345\235\217\347\216\213.png" (100%) rename "src/resources/assets/images/\346\225\243\345\215\216\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222.png" => "images/\346\225\243\345\215\216\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222.png" (100%) rename "src/resources/assets/images/\346\227\272\350\264\242.png" => "images/\346\227\272\350\264\242.png" (100%) rename "src/resources/assets/images/\346\232\264\350\265\260\351\243\237\344\272\272\350\212\261.png" => "images/\346\232\264\350\265\260\351\243\237\344\272\272\350\212\261.png" (100%) rename "src/resources/assets/images/\346\232\264\350\265\260\351\243\237\350\231\253\350\212\261.png" => "images/\346\232\264\350\265\260\351\243\237\350\231\253\350\212\261.png" (100%) rename "src/resources/assets/images/\346\234\227\345\247\206\351\205\222\346\216\250\350\215\220\350\200\205.png" => "images/\346\234\227\345\247\206\351\205\222\346\216\250\350\215\220\350\200\205.png" (100%) rename "src/resources/assets/images/\346\235\260\346\226\257\351\241\277\302\267\345\250\201\345\273\211\345\247\206\346\226\257.png" => "images/\346\235\260\346\226\257\351\241\277\302\267\345\250\201\345\273\211\345\247\206\346\226\257.png" (100%) rename "src/resources/assets/images/\346\236\201\346\270\251\350\222\270\346\261\275\346\210\230\350\275\246.png" => "images/\346\236\201\346\270\251\350\222\270\346\261\275\346\210\230\350\275\246.png" (100%) rename "src/resources/assets/images/\346\236\243\345\244\247\345\210\200.png" => "images/\346\236\243\345\244\247\345\210\200.png" (100%) rename "src/resources/assets/images/\346\236\257\346\234\275\344\271\213\347\247\215.png" => "images/\346\236\257\346\234\275\344\271\213\347\247\215.png" (100%) rename "src/resources/assets/images/\346\243\230\345\205\275.png" => "images/\346\243\230\345\205\275.png" (100%) rename "src/resources/assets/images/\346\256\213\345\205\232\344\271\220\345\233\242\351\274\223\346\211\213.png" => "images/\346\256\213\345\205\232\344\271\220\345\233\242\351\274\223\346\211\213.png" (100%) rename "src/resources/assets/images/\346\256\213\345\205\232\350\220\250\345\205\213\346\226\257\346\211\213.png" => "images/\346\256\213\345\205\232\350\220\250\345\205\213\346\226\257\346\211\213.png" (100%) rename "src/resources/assets/images/\346\260\264\346\211\213\351\207\215\350\211\207.png" => "images/\346\260\264\346\211\213\351\207\215\350\211\207.png" (100%) rename "src/resources/assets/images/\346\260\264\351\201\201\345\277\215\350\200\205.png" => "images/\346\260\264\351\201\201\345\277\215\350\200\205.png" (100%) rename "src/resources/assets/images/\346\262\211\346\262\231.png" => "images/\346\262\211\346\262\231.png" (100%) rename "src/resources/assets/images/\346\262\270\350\241\200\351\252\221\345\243\253\345\233\242\347\262\276\351\224\220.png" => "images/\346\262\270\350\241\200\351\252\221\345\243\253\345\233\242\347\262\276\351\224\220.png" (100%) rename "src/resources/assets/images/\346\263\245\345\262\251\345\260\217\351\230\237\350\267\265\350\241\214\350\200\205.png" => "images/\346\263\245\345\262\251\345\260\217\351\230\237\350\267\265\350\241\214\350\200\205.png" (100%) rename "src/resources/assets/images/\346\263\245\345\262\251\345\267\250\345\203\217.png" => "images/\346\263\245\345\262\251\345\267\250\345\203\217.png" (100%) rename "src/resources/assets/images/\346\267\261\346\272\237\350\243\202\347\244\201\350\200\205.png" => "images/\346\267\261\346\272\237\350\243\202\347\244\201\350\200\205.png" (100%) rename "src/resources/assets/images/\346\270\251\351\241\272\347\232\204\346\255\246\350\243\205\351\251\256\345\205\275.png" => "images/\346\270\251\351\241\272\347\232\204\346\255\246\350\243\205\351\251\256\345\205\275.png" (100%) rename "src/resources/assets/images/\346\270\270\345\207\273\351\230\237\347\233\276\345\215\253.png" => "images/\346\270\270\345\207\273\351\230\237\347\233\276\345\215\253.png" (100%) rename "src/resources/assets/images/\346\271\226\347\225\224\345\277\227\346\204\277\350\200\205.png" => "images/\346\271\226\347\225\224\345\277\227\346\204\277\350\200\205.png" (100%) rename "src/resources/assets/images/\346\272\220\347\237\263\347\225\270\345\217\230\344\275\223.png" => "images/\346\272\220\347\237\263\347\225\270\345\217\230\344\275\223.png" (100%) rename "src/resources/assets/images/\347\201\274\347\203\255\346\272\220\347\237\263\350\231\253.png" => "images/\347\201\274\347\203\255\346\272\220\347\237\263\350\231\253.png" (100%) rename "src/resources/assets/images/\347\202\255\351\225\277\347\237\233.png" => "images/\347\202\255\351\225\277\347\237\233.png" (100%) rename "src/resources/assets/images/\347\202\256\345\207\273\347\273\204\351\225\277.png" => "images/\347\202\256\345\207\273\347\273\204\351\225\277.png" (100%) rename "src/resources/assets/images/\347\202\275\347\204\260\346\272\220\347\237\263\350\231\253.png" => "images/\347\202\275\347\204\260\346\272\220\347\237\263\350\231\253.png" (100%) rename "src/resources/assets/images/\347\213\202\346\232\264\345\256\277\344\270\273\347\273\204\351\225\277.png" => "images/\347\213\202\346\232\264\345\256\277\344\270\273\347\273\204\351\225\277.png" (100%) rename "src/resources/assets/images/\347\213\202\346\232\264\347\232\204\347\214\216\347\213\227pro.png" => "images/\347\213\202\346\232\264\347\232\204\347\214\216\347\213\227pro.png" (100%) rename "src/resources/assets/images/\347\213\231\345\207\273\346\255\245\345\205\265.png" => "images/\347\213\231\345\207\273\346\255\245\345\205\265.png" (100%) rename "src/resources/assets/images/\347\216\211\345\217\214\345\211\221.png" => "images/\347\216\211\345\217\214\345\211\221.png" (100%) rename "src/resources/assets/images/\347\224\260\351\274\267.png" => "images/\347\224\260\351\274\267.png" (100%) rename "src/resources/assets/images/\347\225\270\345\217\230\350\265\230\347\224\237\347\211\251.png" => "images/\347\225\270\345\217\230\350\265\230\347\224\237\347\211\251.png" (100%) rename "src/resources/assets/images/\347\230\264.png" => "images/\347\230\264.png" (100%) rename "src/resources/assets/images/\347\237\277\350\204\211\345\256\210\345\215\253.png" => "images/\347\237\277\350\204\211\345\256\210\345\215\253.png" (100%) rename "src/resources/assets/images/\347\240\201\345\244\264\346\260\264\346\211\213.png" => "images/\347\240\201\345\244\264\346\260\264\346\211\213.png" (100%) rename "src/resources/assets/images/\347\241\254\347\224\262\347\210\252\345\205\275.png" => "images/\347\241\254\347\224\262\347\210\252\345\205\275.png" (100%) rename "src/resources/assets/images/\347\251\272\351\231\215\345\205\265.png" => "images/\347\251\272\351\231\215\345\205\265.png" (100%) rename "src/resources/assets/images/\347\273\210\346\233\262\345\220\210\345\243\260.png" => "images/\347\273\210\346\233\262\345\220\210\345\243\260.png" (100%) rename "src/resources/assets/images/\350\205\220\350\264\245\351\252\221\345\243\253.png" => "images/\350\205\220\350\264\245\351\252\221\345\243\253.png" (100%) rename "src/resources/assets/images/\350\207\252\347\224\261\344\275\243\345\205\265\342\200\234\344\270\273\351\237\263\345\220\211\344\273\226\342\200\235.png" => "images/\350\207\252\347\224\261\344\275\243\345\205\265\342\200\234\344\270\273\351\237\263\345\220\211\344\273\226\342\200\235.png" (100%) rename "src/resources/assets/images/\350\207\252\347\224\261\344\275\243\345\205\265\342\200\234\350\264\235\346\226\257\342\200\235.png" => "images/\350\207\252\347\224\261\344\275\243\345\205\265\342\200\234\350\264\235\346\226\257\342\200\235.png" (100%) rename "src/resources/assets/images/\350\215\222\345\216\237\345\212\253\346\216\240\350\200\205.png" => "images/\350\215\222\345\216\237\345\212\253\346\216\240\350\200\205.png" (100%) rename "src/resources/assets/images/\350\220\250\345\215\241\345\205\271\345\244\247\345\211\221\346\211\213.png" => "images/\350\220\250\345\215\241\345\205\271\345\244\247\345\211\221\346\211\213.png" (100%) rename "src/resources/assets/images/\350\220\250\345\215\241\345\205\271\345\256\277\344\270\273\345\215\253\345\267\242\347\231\276\345\244\253\351\225\277.png" => "images/\350\220\250\345\215\241\345\205\271\345\256\277\344\270\273\345\215\253\345\267\242\347\231\276\345\244\253\351\225\277.png" (100%) rename "src/resources/assets/images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\345\220\236\345\231\254\350\200\205.png" => "images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\345\220\236\345\231\254\350\200\205.png" (100%) rename "src/resources/assets/images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\345\256\277\345\215\253.png" => "images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\345\256\277\345\215\253.png" (100%) rename "src/resources/assets/images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\346\210\230\350\275\246.png" => "images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\346\210\230\350\275\246.png" (100%) rename "src/resources/assets/images/\350\257\241\346\240\270\351\233\206\345\205\273\350\200\205.png" => "images/\350\257\241\346\240\270\351\233\206\345\205\273\350\200\205.png" (100%) rename "src/resources/assets/images/\350\265\217\351\207\221\347\214\216\344\272\272\345\274\251\346\211\213.png" => "images/\350\265\217\351\207\221\347\214\216\344\272\272\345\274\251\346\211\213.png" (100%) rename "src/resources/assets/images/\350\265\217\351\207\221\347\214\216\344\272\272\346\211\260\344\271\261\350\200\205.png" => "images/\350\265\217\351\207\221\347\214\216\344\272\272\346\211\260\344\271\261\350\200\205.png" (100%) rename "src/resources/assets/images/\350\277\267\350\267\257\347\232\204\345\267\250\345\203\217.png" => "images/\350\277\267\350\267\257\347\232\204\345\267\250\345\203\217.png" (100%) rename "src/resources/assets/images/\351\205\270\346\266\262\346\272\220\347\237\263\350\231\253\302\267\316\261.png" => "images/\351\205\270\346\266\262\346\272\220\347\237\263\350\231\253\302\267\316\261.png" (100%) rename "src/resources/assets/images/\351\207\215\350\243\205\351\230\262\345\276\241\350\200\205.png" => "images/\351\207\215\350\243\205\351\230\262\345\276\241\350\200\205.png" (100%) rename "src/resources/assets/images/\351\230\265\345\234\260\345\207\273\344\272\272\346\211\213.png" => "images/\351\230\265\345\234\260\345\207\273\344\272\272\346\211\213.png" (100%) rename "src/resources/assets/images/\351\243\216\346\203\205\350\241\227\342\200\234\346\230\237\346\234\257\345\270\210\342\200\235.png" => "images/\351\243\216\346\203\205\350\241\227\342\200\234\346\230\237\346\234\257\345\270\210\342\200\235.png" (100%) rename "src/resources/assets/images/\351\253\230\345\241\224\346\234\257\345\270\210.png" => "images/\351\253\230\345\241\224\346\234\257\345\270\210.png" (100%) rename "src/resources/assets/images/\351\253\230\346\231\256\345\260\274\345\205\213.png" => "images/\351\253\230\346\231\256\345\260\274\345\205\213.png" (100%) rename "src/resources/assets/images/\351\253\230\347\272\247\346\255\246\350\243\205\344\272\272\345\221\230.png" => "images/\351\253\230\347\272\247\346\255\246\350\243\205\344\272\272\345\221\230.png" (100%) rename "src/resources/assets/images/\351\253\230\350\203\275\346\272\220\347\237\263\350\231\253.png" => "images/\351\253\230\350\203\275\346\272\220\347\237\263\350\231\253.png" (100%) rename "src/resources/assets/images/\351\273\221\346\260\264\346\272\220\347\237\263\350\231\253.png" => "images/\351\273\221\346\260\264\346\272\220\347\237\263\350\231\253.png" (100%) rename src/ui/input_panel_ui.py => input_panel_ui.py (80%) rename src/data/load_data.py => loadData.py (76%) rename src/game/login.py => login.py (72%) rename src/game/maa_adb_connector.py => maa_adb_connector.py (82%) rename {vendor/bin/maafw => maafw}/DirectML.dll (100%) rename {vendor/bin/maafw => maafw}/MaaAdbControlUnit.dll (100%) rename {vendor/bin/maafw => maafw}/MaaAgentClient.dll (100%) rename {vendor/bin/maafw => maafw}/MaaAgentServer.dll (100%) rename {vendor/bin/maafw => maafw}/MaaCustomControlUnit.dll (100%) rename {vendor/bin/maafw => maafw}/MaaFramework.dll (100%) rename {vendor/bin/maafw => maafw}/MaaGamepadControlUnit.dll (100%) rename {vendor/bin/maafw => maafw}/MaaNode.node (100%) rename {vendor/bin/maafw => maafw}/MaaNodeServer.node (100%) rename {vendor/bin/maafw => maafw}/MaaPiCli.exe (100%) rename {vendor/bin/maafw => maafw}/MaaRecordControlUnit.dll (100%) rename {vendor/bin/maafw => maafw}/MaaReplayControlUnit.dll (100%) rename {vendor/bin/maafw => maafw}/MaaToolkit.dll (100%) rename {vendor/bin/maafw => maafw}/MaaUtils.dll (100%) rename {vendor/bin/maafw => maafw}/MaaWin32ControlUnit.dll (100%) rename {vendor/bin/maafw => maafw}/ViGEmClient.dll (100%) rename {vendor/bin/maafw => maafw}/fastdeploy_ppocr_maa.dll (100%) rename {vendor/bin/maafw => maafw}/onnxruntime_maa.dll (100%) rename {vendor/bin/maafw => maafw}/opencv_world4_maa.dll (100%) rename {vendor/bin/maafw => maafw}/plugins/MaaPluginDemo.dll (100%) create mode 100644 main_old.py rename {src/analysis => models}/__init__.py (100%) rename {src/models => models}/model.py (94%) rename {src/models => models}/muon.py (75%) create mode 100644 models/update.md rename src/resources/data/monster.csv => monster.csv (100%) rename src/resources/data/monster_greenvine.csv => monster_greenvine.csv (100%) rename {vendor/bin/platform-tools => platform-tools}/AdbWinApi.dll (100%) rename {vendor/bin/platform-tools => platform-tools}/AdbWinUsbApi.dll (100%) rename {vendor/bin/platform-tools => platform-tools}/NOTICE.txt (100%) rename {vendor/bin/platform-tools => platform-tools}/adb.exe (100%) rename {vendor/bin/platform-tools => platform-tools}/etc1tool.exe (100%) rename {vendor/bin/platform-tools => platform-tools}/fastboot.exe (100%) rename {vendor/bin/platform-tools => platform-tools}/hprof-conv.exe (100%) rename {vendor/bin/platform-tools => platform-tools}/libwinpthread-1.dll (100%) rename {vendor/bin/platform-tools => platform-tools}/make_f2fs.exe (100%) rename {vendor/bin/platform-tools => platform-tools}/make_f2fs_casefold.exe (100%) rename {vendor/bin/platform-tools => platform-tools}/mke2fs.conf (100%) rename {vendor/bin/platform-tools => platform-tools}/mke2fs.exe (100%) rename {vendor/bin/platform-tools => platform-tools}/source.properties (100%) rename {vendor/bin/platform-tools => platform-tools}/sqlite3.exe (100%) rename src/models/predict.py => predict.py (61%) rename src/models/predict_onnx.py => predict_onnx.py (74%) rename src/recognition/recognize.py => recognize.py (81%) rename src/analysis/sim_mc.py => sim_mc.py (67%) rename src/analysis/similar_history_match.py => similar_history_match.py (74%) rename src/ui/similar_history_match_ui.py => simular_history_match_ui.py (85%) create mode 100644 simulator/.gitignore rename {src/simulation => simulator}/README.md (100%) rename {src/core => simulator}/__init__.py (100%) rename {src/simulation => simulator}/arknights.csv (100%) rename {src/simulation => simulator}/battle_field.py (60%) rename {src/simulation => simulator}/elemental.py (82%) rename {src/simulation => simulator}/monsters.json (100%) rename {src/simulation => simulator}/monsters.py (80%) rename {src/simulation => simulator}/projectiles.py (52%) create mode 100644 simulator/scene.json rename {src/simulation => simulator}/simulate.py (60%) create mode 100644 simulator/stats.py rename {src/simulation => simulator}/utils.py (80%) rename {src/simulation => simulator}/vector2d.py (85%) rename {src/simulation => simulator}/zone.py (70%) create mode 100644 specialmonster.py delete mode 100644 src/analysis/winning_rate_statistics.py delete mode 100644 src/core/paths.py delete mode 100644 src/data/__init__.py delete mode 100644 src/game/__init__.py delete mode 100644 src/models/__init__.py delete mode 100644 src/recognition/__init__.py delete mode 100644 src/recognition/specialmonster.py delete mode 100644 src/resources/config/maa_option.json delete mode 100644 src/simulation/__init__.py delete mode 100644 src/simulation/scene.json delete mode 100644 src/simulation/stats.py delete mode 100644 src/tools/battlefield_composite/extract_webm_frames.py delete mode 100644 src/tools/data_washer_new.py delete mode 100644 src/ui/__init__.py delete mode 100644 src/utils/__init__.py rename {src/tools => tools}/HumanDataCheck.py (58%) rename {src/tools => tools}/battlefield_composite/.gitignore (100%) rename {src/tools => tools}/battlefield_composite/battlefield_composite.py (87%) create mode 100644 tools/battlefield_composite/extract_webm_frames.py rename {src/tools => tools}/battlefield_composite/monster_images/IM-1.png (100%) rename {src/tools => tools}/battlefield_composite/monster_images/battlefield_empty.jpg (100%) rename "src/tools/battlefield_composite/monster_images/\342\200\234\345\244\215\344\273\207\350\200\205\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\342\200\234\345\244\215\344\273\207\350\200\205\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\342\200\234\345\272\236\350\264\235\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\342\200\234\345\272\236\350\264\235\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\342\200\234\346\212\225\347\237\263\346\234\272\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-A_Move-x1.webm" => "tools/battlefield_composite/monster_images/\342\200\234\346\212\225\347\237\263\346\234\272\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-A_Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\342\200\234\350\207\252\345\234\250\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\342\200\234\350\207\252\345\234\250\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\342\200\234\351\222\263\351\222\263\347\224\237\351\243\216\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\342\200\234\351\222\263\351\222\263\347\224\237\351\243\216\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\342\200\234\351\227\250\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\342\200\234\351\227\250\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\342\200\234\351\230\277\345\222\254\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\342\200\234\351\230\277\345\222\254\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\345\206\260\345\216\237\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\345\206\260\345\216\237\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\345\206\260\347\210\206\346\272\220\347\237\263\350\231\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\345\206\260\347\210\206\346\272\220\347\237\263\350\231\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\345\217\215\350\243\205\347\224\262\346\255\245\345\205\265-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_A-x1.webm" => "tools/battlefield_composite/monster_images/\345\217\215\350\243\205\347\224\262\346\255\245\345\205\265-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_A-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\345\217\230\345\274\202\345\267\250\345\262\251\350\233\233-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\345\217\230\345\274\202\345\267\250\345\262\251\350\233\233-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\345\221\274\345\225\270\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\345\221\274\345\225\270\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\345\233\272\346\265\267\345\207\277\347\237\263\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\345\233\272\346\265\267\345\207\277\347\237\263\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\345\244\215\344\273\207\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\345\244\215\344\273\207\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\345\256\277\344\270\273\346\213\276\350\215\222\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\345\256\277\344\270\273\346\213\276\350\215\222\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\345\257\214\350\220\245\345\205\273\347\232\204\347\251\277\345\210\272\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\345\257\214\350\220\245\345\205\273\347\232\204\347\251\277\345\210\272\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\345\261\261\346\265\267\344\274\227\347\252\245\351\255\205\344\272\272-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\345\261\261\346\265\267\344\274\227\347\252\245\351\255\205\344\272\272-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\345\261\261\351\233\252\351\254\274-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\345\261\261\351\233\252\351\254\274-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\345\274\247\345\205\211\351\224\213\345\215\253\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\345\274\247\345\205\211\351\224\213\345\215\253\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\346\211\216\347\275\227\357\274\214\342\200\234\347\213\274\344\271\213\344\270\273\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-A_Move-x1.webm" => "tools/battlefield_composite/monster_images/\346\211\216\347\275\227\357\274\214\342\200\234\347\213\274\344\271\213\344\270\273\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-A_Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\346\213\263\346\211\213\345\233\232\347\212\257-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_red-x1.webm" => "tools/battlefield_composite/monster_images/\346\213\263\346\211\213\345\233\232\347\212\257-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_red-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\346\217\220\344\272\232\345\215\241\344\271\214\345\245\275\346\210\230\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\346\217\220\344\272\232\345\215\241\344\271\214\345\245\275\346\210\230\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\346\217\220\344\272\232\345\215\241\344\271\214\347\240\264\345\235\217\347\216\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\346\217\220\344\272\232\345\215\241\344\271\214\347\240\264\345\235\217\347\216\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\346\225\243\345\215\216\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\346\225\243\345\215\216\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\346\234\227\345\247\206\351\205\222\346\216\250\350\215\220\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\346\234\227\345\247\206\351\205\222\346\216\250\350\215\220\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\346\234\250\350\243\202\346\210\230\345\243\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\346\234\250\350\243\202\346\210\230\345\243\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\346\235\260\346\226\257\351\241\277\302\267\345\250\201\345\273\211\345\247\206\346\226\257-\351\273\230\350\256\244-\346\210\230\346\226\227-C1_Move-x1.webm" => "tools/battlefield_composite/monster_images/\346\235\260\346\226\257\351\241\277\302\267\345\250\201\345\273\211\345\247\206\346\226\257-\351\273\230\350\256\244-\346\210\230\346\226\227-C1_Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\346\237\223\346\261\241\350\272\257\345\243\263-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\346\237\223\346\261\241\350\272\257\345\243\263-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\346\256\213\345\205\232\350\220\250\345\205\213\346\226\257\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\346\256\213\345\205\232\350\220\250\345\205\213\346\226\257\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\346\262\270\350\241\200\351\252\221\345\243\253\345\233\242\347\262\276\351\224\220-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\346\262\270\350\241\200\351\252\221\345\243\253\345\233\242\347\262\276\351\224\220-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\346\263\245\345\262\251\345\267\250\345\203\217-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\346\263\245\345\262\251\345\267\250\345\203\217-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\346\267\261\346\272\237\350\243\202\347\244\201\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\346\267\261\346\272\237\350\243\202\347\244\201\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\346\270\251\351\241\272\347\232\204\346\255\246\350\243\205\351\251\256\345\205\275-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\346\270\251\351\241\272\347\232\204\346\255\246\350\243\205\351\251\256\345\205\275-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\346\271\226\347\225\224\345\277\227\346\204\277\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\346\271\226\347\225\224\345\277\227\346\204\277\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\346\272\220\347\237\263\347\225\270\345\217\230\344\275\223-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\346\272\220\347\237\263\347\225\270\345\217\230\344\275\223-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\347\201\260\345\260\276\351\246\231\344\270\273-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\347\201\260\345\260\276\351\246\231\344\270\273-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\347\202\256\345\207\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Attack-x1.webm" => "tools/battlefield_composite/monster_images/\347\202\256\345\207\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Attack-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\347\202\256\345\207\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\347\202\256\345\207\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\347\203\210\351\205\222\347\272\247\351\206\222\351\205\222\345\212\251\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\347\203\210\351\205\222\347\272\247\351\206\222\351\205\222\345\212\251\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\347\213\202\346\232\264\345\256\277\344\270\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\347\213\202\346\232\264\345\256\277\344\270\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\347\213\202\346\232\264\347\232\204\347\214\216\347\213\227pro-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" => "tools/battlefield_composite/monster_images/\347\213\202\346\232\264\347\232\204\347\214\216\347\213\227pro-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\347\224\260\351\274\267\345\212\233\345\243\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\347\224\260\351\274\267\345\212\233\345\243\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\347\237\277\350\204\211\345\256\210\345\215\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\347\237\277\350\204\211\345\256\210\345\215\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\347\245\236\345\260\204\346\211\213\345\233\232\347\212\257-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\347\245\236\345\260\204\346\211\213\345\233\232\347\212\257-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\345\244\247\345\211\221\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" => "tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\345\244\247\345\211\221\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\345\255\220\350\243\224\351\223\276\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\345\255\220\350\243\224\351\223\276\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\347\216\213\345\272\255\345\206\233\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\347\216\213\345\272\255\345\206\233\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\351\200\220\350\205\220\345\205\275-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\351\200\220\350\205\220\345\205\275-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\351\205\270\346\266\262\346\272\220\347\237\263\350\231\253\302\267\316\261-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" => "tools/battlefield_composite/monster_images/\351\205\270\346\266\262\346\272\220\347\237\263\350\231\253\302\267\316\261-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\351\207\215\350\243\205\351\230\262\345\276\241\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" => "tools/battlefield_composite/monster_images/\351\207\215\350\243\205\351\230\262\345\276\241\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\351\230\265\345\234\260\345\207\273\344\272\272\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\351\230\265\345\234\260\345\207\273\344\272\272\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\351\243\216\346\203\205\350\241\227\342\200\234\346\230\237\346\234\257\345\270\210\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\351\243\216\346\203\205\350\241\227\342\200\234\346\230\237\346\234\257\345\270\210\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\351\253\230\345\241\224\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\351\253\230\345\241\224\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\351\253\230\346\231\256\345\260\274\345\205\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\351\253\230\346\231\256\345\260\274\345\205\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\351\253\230\347\272\247\346\255\246\350\243\205\344\272\272\345\221\230-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\351\253\230\347\272\247\346\255\246\350\243\205\344\272\272\345\221\230-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "src/tools/battlefield_composite/monster_images/\351\253\230\350\203\275\346\272\220\347\237\263\350\231\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "tools/battlefield_composite/monster_images/\351\253\230\350\203\275\346\272\220\347\237\263\350\231\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename {src/tools => tools}/battlefield_recognize/class_to_idx.json (100%) rename "src/tools/battlefield_recognize/\345\234\272\345\234\260\350\257\206\345\210\253\345\255\227\346\256\265\350\257\264\346\230\216.txt" => "tools/battlefield_recognize/\345\234\272\345\234\260\350\257\206\345\210\253\345\255\227\346\256\265\350\257\264\346\230\216.txt" (100%) rename {src/tools => tools}/convert_model.py (70%) rename {src/tools => tools}/data_cleaning.py (97%) rename {src/tools => tools}/data_cleaning_with_field_recognize.py (61%) rename {src/tools => tools}/data_cleaning_with_field_recognize_gpu.py (61%) rename {src/tools => tools}/data_nanWriter.py (94%) create mode 100644 tools/data_washer_new.py rename {src/tools => tools}/package.py (93%) rename src/models/train.py => train.py (70%) rename src/utils/unit.py => unit.py (94%) rename src/models/val.py => val.py (71%) rename src/game/winrt_capture.py => winrt_capture.py (90%) rename iron_shark_launcher.bat => "\343\200\220\350\204\232\346\234\254\343\200\221\351\223\201\351\262\250\351\261\274\345\220\257\345\212\250\357\274\201(\345\270\246PyTorch\347\211\210\346\234\254\351\200\211\346\213\251\347\211\210).bat" (62%) diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index eadd33c..73a4c53 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -24,13 +24,13 @@ jobs: run: uv sync --group dev - name: Build with PyInstaller - run: uv run src/tools/package.py + run: uv run tools/package.py - name: Upload build artifact uses: actions/upload-artifact@v7 with: name: cannotmax-build - path: build/dist/main/ + path: output/main/ retention-days: 1 release: diff --git a/.gitignore b/.gitignore index 14e3469..5781d3d 100644 --- a/.gitignore +++ b/.gitignore @@ -30,17 +30,27 @@ __pycache__/ # 5. 模型权重与构建产物 # =========================== # 模型文件 -/models/ +models/*.png +*.pth +*.onnx +*.onnx.data # 构建与输出目录 -/build/ +build/ +output/ # =========================== # 6. 数据集与媒体资源 # =========================== # 基础数据目录 -src/resources/assets/images/nums/ -/data/ +data/images/ +data/[0-9][0-9][0-9][0-9]_[0-1][0-9]_[0-3][0-9]__[0-2][0-9]_[0-5][0-9]_[0-5][0-9]/ +images/nums/ + +# 训练相关数据 +data_train/tmp/ +data_train/package/*.zip +data_train/images/ # 单独的截图文件 screenshot.png @@ -58,4 +68,5 @@ tmp # =========================== # 8. 其他杂项文件 # =========================== +arknights.csv multi_ports.txt diff --git a/WinningRate_Statistics.py b/WinningRate_Statistics.py new file mode 100644 index 0000000..c6d9aad --- /dev/null +++ b/WinningRate_Statistics.py @@ -0,0 +1,942 @@ +import pandas as pd +import numpy as np +from pathlib import Path +from math import sqrt +import csv +from collections import defaultdict +from config import MONSTER_COUNT, FIELD_FEATURE_COUNT, MONSTER_DATA + +FIELD_FEATURE_COUNT=0 +def load_data(): + """加载数据""" + df = pd.read_csv('arknights.csv', header=None, low_memory=False) + # 设置列名 + monster_cols_left = [f'L{i+1}' for i in range(MONSTER_COUNT)] + field_cols_left = [f'FL{i+1}' for i in range(FIELD_FEATURE_COUNT)] + monster_cols_right = [f'R{i+1}' for i in range(MONSTER_COUNT)] + field_cols_right = [f'FR{i+1}' for i in range(FIELD_FEATURE_COUNT)] + + df.columns = monster_cols_left + field_cols_left + monster_cols_right + field_cols_right + ['Result', 'ImgPath'] + return df + + +def get_monster_name(monster_id): + """根据怪物ID获取怪物名称""" + if monster_id in MONSTER_DATA.index: + return MONSTER_DATA.loc[monster_id]['名称'] + return f'怪物{monster_id}' + + +def get_monster_original_name(monster_id): + """根据怪物ID获取怪物原始名称(用于匹配图片)""" + if monster_id in MONSTER_DATA.index: + return MONSTER_DATA.loc[monster_id]['原始名称'] + return f'怪物{monster_id}' + + +def calculate_all_monster_win_rates(df): + """计算所有怪物的胜率""" + monster_stats = {} + total_matches = len(df) + + for i in range(1, MONSTER_COUNT + 1): + try: + # 转换数据类型并过滤 + df[f'L{i}'] = pd.to_numeric(df[f'L{i}'], errors='coerce').fillna(0) + df[f'R{i}'] = pd.to_numeric(df[f'R{i}'], errors='coerce').fillna(0) + + # 左方统计 + left_games = df[df[f'L{i}'] != 0] + left_wins = len(left_games[left_games['Result'] == 'L']) + left_total = len(left_games) + + # 右方统计 + right_games = df[df[f'R{i}'] != 0] + right_wins = len(right_games[right_games['Result'] == 'R']) + right_total = len(right_games) + except Exception as e: + print(f"处理怪物{i}时出错: {e}") + continue + + # 合并统计 + total_games = left_total + right_total + total_wins = left_wins + right_wins + + if total_games > 0: + monster_name = get_monster_name(i) + win_rate = total_wins / total_games + participation_rate = total_games / total_matches if total_matches > 0 else 0 + monster_stats[monster_name] = { + '怪物ID': i, + '胜场': total_wins, + '总场数': total_games, + '胜率': win_rate, + '参战率': participation_rate + } + + return pd.DataFrame(monster_stats).T.sort_values('胜率', ascending=False) + + +def analyze_monster_combinations(df): + """分析怪物配合效果""" + # 初始化数据结构 + single_stats = defaultdict(lambda: {'appearances': 0, 'wins': 0}) + pair_stats = defaultdict(lambda: {'co_occurrences': 0, 'co_wins': 0}) + + for _, record in df.iterrows(): + victory_side = record['Result'] + + # 获取左右两方的怪物 + left_monsters = [] + right_monsters = [] + + for i in range(1, MONSTER_COUNT + 1): + try: + left_val = float(record[f'L{i}']) if pd.notna(record[f'L{i}']) else 0 + right_val = float(record[f'R{i}']) if pd.notna(record[f'R{i}']) else 0 + + if left_val > 0: + left_monsters.append(i) + if right_val > 0: + right_monsters.append(i) + except (ValueError, TypeError): + continue + + # 确定胜利队伍和失败队伍 + if victory_side == 'L': + win_team, lose_team = left_monsters, right_monsters + else: + win_team, lose_team = right_monsters, left_monsters + + # 更新单怪统计(胜利方) + for monster in win_team: + single_stats[monster]['appearances'] += 1 + single_stats[monster]['wins'] += 1 + + # 更新单怪统计(失败方) + for monster in lose_team: + single_stats[monster]['appearances'] += 1 + + # 更新双怪组合统计(胜利方) + for i in range(len(win_team)): + for j in range(i+1, len(win_team)): + x, y = sorted((win_team[i], win_team[j])) + pair_stats[(x, y)]['co_occurrences'] += 1 + pair_stats[(x, y)]['co_wins'] += 1 + + # 更新双怪组合统计(失败方) + for i in range(len(lose_team)): + for j in range(i+1, len(lose_team)): + x, y = sorted((lose_team[i], lose_team[j])) + pair_stats[(x, y)]['co_occurrences'] += 1 + + # 计算最佳配合 + results = [] + total_battles = len(df) + + for (x, y), stats in pair_stats.items(): + if stats['co_occurrences'] < 10: # 过滤低频组合 + continue + + # 组合胜率 + xy_win_rate = stats['co_wins'] / stats['co_occurrences'] + + # 单怪胜率 + if single_stats[x]['appearances'] > 0 and single_stats[y]['appearances'] > 0: + x_win_rate = single_stats[x]['wins'] / single_stats[x]['appearances'] + y_win_rate = single_stats[y]['wins'] / single_stats[y]['appearances'] + + # 提升度 - 简化计算,不使用卡方检验 + expected_win_rate = sqrt(x_win_rate * y_win_rate) + lift = xy_win_rate / expected_win_rate if expected_win_rate > 0 else 0 + + if lift > 1.1 and xy_win_rate > max(x_win_rate, y_win_rate) and stats['co_occurrences'] >= 20: + x_name = get_monster_name(x) + y_name = get_monster_name(y) + results.append({ + '组合': f'{x_name}+{y_name}', + '怪物1': x_name, + '怪物2': y_name, + 'ID1': x, + 'ID2': y, + '提升度': lift, + '组合胜率': xy_win_rate, + '出场次数': stats['co_occurrences'], + '获胜次数': stats['co_wins'] + }) + + # 按提升度排序 + results.sort(key=lambda x: -x['提升度']) + return pd.DataFrame(results) + + +def find_countered_monsters(df): + """寻找被克制的怪物前五个""" + counter_stats = defaultdict(lambda: {'total_matchups': 0, 'losses': 0}) + + for _, record in df.iterrows(): + victory_side = record['Result'] + + # 获取左右两方的怪物 + left_monsters = [] + right_monsters = [] + + for i in range(1, MONSTER_COUNT + 1): + try: + left_val = float(record[f'L{i}']) if pd.notna(record[f'L{i}']) else 0 + right_val = float(record[f'R{i}']) if pd.notna(record[f'R{i}']) else 0 + + if left_val > 0: + left_monsters.append(i) + if right_val > 0: + right_monsters.append(i) + except (ValueError, TypeError): + continue + + # 分析对战情况 + for left_monster in left_monsters: + for right_monster in right_monsters: + # 左方怪物的统计 + counter_stats[left_monster]['total_matchups'] += 1 + if victory_side == 'R': # 左方败北 + counter_stats[left_monster]['losses'] += 1 + + # 右方怪物的统计 + counter_stats[right_monster]['total_matchups'] += 1 + if victory_side == 'L': # 右方败北 + counter_stats[right_monster]['losses'] += 1 + + # 计算被克制率 + countered_results = [] + for monster_id, stats in counter_stats.items(): + if stats['total_matchups'] >= 20: # 至少20场对战 + loss_rate = stats['losses'] / stats['total_matchups'] + monster_name = get_monster_name(monster_id) + countered_results.append({ + '怪物': monster_name, + '怪物ID': monster_id, + '被克制率': loss_rate, + '败场': stats['losses'], + '总对战数': stats['total_matchups'] + }) + + # 按被克制率排序(降序) + countered_results.sort(key=lambda x: -x['被克制率']) + return pd.DataFrame(countered_results[:5]) + + +def analyze_individual_monster_relations(df): + """分析每个怪物的详细关系:最佳队友、克制关系、被克制关系""" + monster_relations = {} + + # 初始化数据结构 + single_stats = defaultdict(lambda: {'appearances': 0, 'wins': 0}) + pair_stats = defaultdict(lambda: {'co_occurrences': 0, 'co_wins': 0}) + counter_stats = defaultdict(lambda: defaultdict(lambda: {'matchups': 0, 'wins': 0})) + + for _, record in df.iterrows(): + victory_side = record['Result'] + + # 获取左右两方的怪物 + left_monsters = [] + right_monsters = [] + + for i in range(1, MONSTER_COUNT + 1): + try: + left_val = float(record[f'L{i}']) if pd.notna(record[f'L{i}']) else 0 + right_val = float(record[f'R{i}']) if pd.notna(record[f'R{i}']) else 0 + + if left_val > 0: + left_monsters.append(i) + if right_val > 0: + right_monsters.append(i) + except (ValueError, TypeError): + continue + + # 确定胜利队伍和失败队伍 + if victory_side == 'L': + win_team, lose_team = left_monsters, right_monsters + else: + win_team, lose_team = right_monsters, left_monsters + + # 更新单怪统计 + for monster in win_team: + single_stats[monster]['appearances'] += 1 + single_stats[monster]['wins'] += 1 + + for monster in lose_team: + single_stats[monster]['appearances'] += 1 + + # 更新队友统计 + for i in range(len(win_team)): + for j in range(i+1, len(win_team)): + x, y = sorted((win_team[i], win_team[j])) + pair_stats[(x, y)]['co_occurrences'] += 1 + pair_stats[(x, y)]['co_wins'] += 1 + + for i in range(len(lose_team)): + for j in range(i+1, len(lose_team)): + x, y = sorted((lose_team[i], lose_team[j])) + pair_stats[(x, y)]['co_occurrences'] += 1 + + # 更新克制关系统计 + for winner in win_team: + for loser in lose_team: + counter_stats[winner][loser]['matchups'] += 1 + counter_stats[winner][loser]['wins'] += 1 + counter_stats[loser][winner]['matchups'] += 1 + + # 为每个怪物分析关系 + for monster_id in range(1, MONSTER_COUNT + 1): + monster_name = get_monster_name(monster_id) + + if single_stats[monster_id]['appearances'] < 10: # 数据量太少 + continue + + # 分析最佳队友 + best_teammates = [] + for (x, y), stats in pair_stats.items(): + if x == monster_id or y == monster_id: + partner_id = y if x == monster_id else x + if stats['co_occurrences'] >= 5: # 至少5次合作 + combo_win_rate = stats['co_wins'] / stats['co_occurrences'] + + # 计算提升度 + if (single_stats[monster_id]['appearances'] > 0 and + single_stats[partner_id]['appearances'] > 0): + monster_win_rate = single_stats[monster_id]['wins'] / single_stats[monster_id]['appearances'] + partner_win_rate = single_stats[partner_id]['wins'] / single_stats[partner_id]['appearances'] + expected_win_rate = sqrt(monster_win_rate * partner_win_rate) + + if expected_win_rate > 0: + lift = combo_win_rate / expected_win_rate + if lift > 1.0: + best_teammates.append({ + 'partner_id': partner_id, + 'partner_name': get_monster_name(partner_id), + 'lift': lift, + 'combo_win_rate': combo_win_rate, + 'occurrences': stats['co_occurrences'] + }) + + best_teammates.sort(key=lambda x: -x['lift']) + + # 分析克制关系 + counters = [] # 该怪物克制的 + countered_by = [] # 克制该怪物的 + + for opponent_id in counter_stats[monster_id]: + stats = counter_stats[monster_id][opponent_id] + if stats['matchups'] >= 5: + win_rate = stats['wins'] / stats['matchups'] + if win_rate > 0.6: # 胜率超过60%认为克制 + counters.append({ + 'opponent_id': opponent_id, + 'opponent_name': get_monster_name(opponent_id), + 'win_rate': win_rate, + 'matchups': stats['matchups'] + }) + + for opponent_id in range(1, MONSTER_COUNT + 1): + if opponent_id in counter_stats and monster_id in counter_stats[opponent_id]: + stats = counter_stats[opponent_id][monster_id] + if stats['matchups'] >= 5: + lose_rate = stats['wins'] / stats['matchups'] + if lose_rate > 0.6: # 对方胜率超过60%认为被克制 + countered_by.append({ + 'opponent_id': opponent_id, + 'opponent_name': get_monster_name(opponent_id), + 'lose_rate': lose_rate, + 'matchups': stats['matchups'] + }) + + counters.sort(key=lambda x: -x['win_rate']) + countered_by.sort(key=lambda x: -x['lose_rate']) + + monster_relations[monster_id] = { + 'name': monster_name, + 'best_teammates': best_teammates[:3], + 'counters': counters[:3], + 'countered_by': countered_by[:3] + } + + return monster_relations + + +def get_terrain_feature_columns(): + """获取地形特征列名""" + import json + import re + from collections import defaultdict + + try: + # 加载类别映射 + class_map_path = "tools/battlefield_recognize/class_to_idx.json" + with open(class_map_path, 'r', encoding='utf-8') as f: + class_to_idx = json.load(f) + + # 使用与data_cleaning_with_field_recognize_gpu.py相同的逻辑 + grouped_elements = defaultdict(list) + for class_name in class_to_idx.keys(): + if class_name.endswith('_none'): + continue + condensed_name = re.sub(r'_left_', '_', class_name) + condensed_name = re.sub(r'_right_', '_', condensed_name) + grouped_elements[condensed_name].append(class_name) + + # 返回排序后的特征列名 + return sorted(grouped_elements.keys()) + except Exception as e: + print(f"无法获取地形特征列名,使用默认值: {e}") + # 如果无法获取,返回默认列表 + return [ + "altar_vertical_altar", "block_parallel_block", "block_vertical_altar_shape1", + "block_vertical_altar_shape2", "block_vertical_block_shape1", "block_vertical_block_shape2", + "coil_narrow_coil", "coil_wide_coil", "crossbow_top_crossbow", + "fire_side_crossbow", "fire_side_fire", "fire_top_fire" + ] + + +def analyze_terrain_effects(df): + """分析地形对怪物的影响""" + terrain_effects = [] + + # 获取实际的地形特征列名 + terrain_feature_columns = get_terrain_feature_columns() + + # 地形显示名称映射 + terrain_display_mapping = { + "altar_vertical_altar": "垂直祭坛", + "block_parallel_block": "平行方块阻挡", + "block_vertical_altar_shape1": "垂直祭坛形阻挡1", + "block_vertical_altar_shape2": "垂直祭坛形阻挡2", + "block_vertical_block_shape1": "垂直方块阻挡1", + "block_vertical_block_shape2": "垂直方块阻挡2", + "coil_narrow_coil": "窄型线圈装置", + "coil_wide_coil": "宽型线圈装置", + "crossbow_top_crossbow": "顶部弩炮", + "fire_side_crossbow": "侧边弩炮", + "fire_side_fire": "侧边火炮", + "fire_top_fire": "顶部火炮" + } + + for terrain_idx, terrain_key in enumerate(terrain_feature_columns): + terrain_name = terrain_display_mapping.get(terrain_key, terrain_key) + + for monster_idx in range(1, MONSTER_COUNT + 1): + monster_name = get_monster_name(monster_idx) + + # 转换数据类型 + df[f'FL{terrain_idx+1}'] = pd.to_numeric(df[f'FL{terrain_idx+1}'], errors='coerce').fillna(0) + df[f'FR{terrain_idx+1}'] = pd.to_numeric(df[f'FR{terrain_idx+1}'], errors='coerce').fillna(0) + df[f'L{monster_idx}'] = pd.to_numeric(df[f'L{monster_idx}'], errors='coerce').fillna(0) + df[f'R{monster_idx}'] = pd.to_numeric(df[f'R{monster_idx}'], errors='coerce').fillna(0) + + # 有地形时的表现 + terrain_left_games = df[(df[f'FL{terrain_idx+1}'] == 1) & (df[f'L{monster_idx}'] > 0)] + terrain_right_games = df[(df[f'FR{terrain_idx+1}'] == 1) & (df[f'R{monster_idx}'] > 0)] + + terrain_total = len(terrain_left_games) + len(terrain_right_games) + if terrain_total < 5: # 数据量太少 + continue + + terrain_wins = len(terrain_left_games[terrain_left_games['Result'] == 'L']) + \ + len(terrain_right_games[terrain_right_games['Result'] == 'R']) + terrain_win_rate = terrain_wins / terrain_total + + # 无地形时的表现 + normal_left_games = df[(df[f'FL{terrain_idx+1}'] == 0) & (df[f'L{monster_idx}'] > 0)] + normal_right_games = df[(df[f'FR{terrain_idx+1}'] == 0) & (df[f'R{monster_idx}'] > 0)] + + normal_total = len(normal_left_games) + len(normal_right_games) + if normal_total < 5: + continue + + normal_wins = len(normal_left_games[normal_left_games['Result'] == 'L']) + \ + len(normal_right_games[normal_right_games['Result'] == 'R']) + normal_win_rate = normal_wins / normal_total + + # 计算影响程度 + effect = terrain_win_rate - normal_win_rate + + if abs(effect) >= 0.05: # 胜率差异超过5%才记录 + terrain_effects.append({ + '地形': terrain_name, + '怪物': monster_name, + '怪物ID': monster_idx, + '地形胜率': terrain_win_rate, + '普通胜率': normal_win_rate, + '影响程度': effect, + '地形场次': terrain_total, + '普通场次': normal_total + }) + + # 按影响程度绝对值排序 + terrain_effects.sort(key=lambda x: -abs(x['影响程度'])) + return pd.DataFrame(terrain_effects[:20]) # 增加到前20个 + + +def analyze_device_counter_effects(df): + """分析五个装置对怪物的克制效果""" + device_counter_results = {} + + # 获取实际的地形特征列名 + terrain_feature_columns = get_terrain_feature_columns() + + # 定义五个装置类别及其对应的地形特征 + device_categories = { + 'altar': { + 'name': '祭坛', + 'features': [f for f in terrain_feature_columns if 'altar' in f], + 'description': '祭坛类装置' + }, + 'block': { + 'name': '箱子/阻挡', + 'features': [f for f in terrain_feature_columns if 'block' in f], + 'description': '方块阻挡类装置' + }, + 'coil': { + 'name': '电桩', + 'features': [f for f in terrain_feature_columns if 'coil' in f], + 'description': '线圈电桩装置' + }, + 'crossbow': { + 'name': '弩箭', + 'features': [f for f in terrain_feature_columns if 'crossbow' in f], + 'description': '弩炮装置' + }, + 'fire': { + 'name': '火炮', + 'features': [f for f in terrain_feature_columns if 'fire' in f], + 'description': '火炮装置' + } + } + + for device_key, device_info in device_categories.items(): + device_name = device_info['name'] + device_features = device_info['features'] + + if not device_features: + continue + + device_effects = [] + + # 对每个怪物分析该装置的克制效果 + for monster_idx in range(1, MONSTER_COUNT + 1): + monster_name = get_monster_name(monster_idx) + + # 收集该装置所有特征的统计数据 + total_device_games = 0 + total_device_wins = 0 + total_normal_games = 0 + total_normal_wins = 0 + + for terrain_key in device_features: + if terrain_key not in terrain_feature_columns: + continue + + terrain_idx = terrain_feature_columns.index(terrain_key) + + # 转换数据类型 + df[f'FL{terrain_idx+1}'] = pd.to_numeric(df[f'FL{terrain_idx+1}'], errors='coerce').fillna(0) + df[f'FR{terrain_idx+1}'] = pd.to_numeric(df[f'FR{terrain_idx+1}'], errors='coerce').fillna(0) + df[f'L{monster_idx}'] = pd.to_numeric(df[f'L{monster_idx}'], errors='coerce').fillna(0) + df[f'R{monster_idx}'] = pd.to_numeric(df[f'R{monster_idx}'], errors='coerce').fillna(0) + + # 有该装置时的表现 + device_left_games = df[(df[f'FL{terrain_idx+1}'] == 1) & (df[f'L{monster_idx}'] > 0)] + device_right_games = df[(df[f'FR{terrain_idx+1}'] == 1) & (df[f'R{monster_idx}'] > 0)] + + device_games_count = len(device_left_games) + len(device_right_games) + device_wins_count = len(device_left_games[device_left_games['Result'] == 'L']) + \ + len(device_right_games[device_right_games['Result'] == 'R']) + + # 无该装置时的表现 + normal_left_games = df[(df[f'FL{terrain_idx+1}'] == 0) & (df[f'L{monster_idx}'] > 0)] + normal_right_games = df[(df[f'FR{terrain_idx+1}'] == 0) & (df[f'R{monster_idx}'] > 0)] + + normal_games_count = len(normal_left_games) + len(normal_right_games) + normal_wins_count = len(normal_left_games[normal_left_games['Result'] == 'L']) + \ + len(normal_right_games[normal_right_games['Result'] == 'R']) + + total_device_games += device_games_count + total_device_wins += device_wins_count + total_normal_games += normal_games_count + total_normal_wins += normal_wins_count + + # 计算整体效果 + if total_device_games >= 10 and total_normal_games >= 10: # 确保有足够的数据 + device_win_rate = total_device_wins / total_device_games + normal_win_rate = total_normal_wins / total_normal_games + effect = device_win_rate - normal_win_rate + + # 计算克制程度(负值表示被该装置克制) + counter_effect = -effect # 装置对怪物的克制效果 + + if abs(effect) >= 0.05: # 胜率差异超过5%才记录 + device_effects.append({ + '怪物': monster_name, + '怪物ID': monster_idx, + '装置胜率': device_win_rate, + '普通胜率': normal_win_rate, + '克制程度': counter_effect, # 正值表示被该装置克制 + '装置场次': total_device_games, + '普通场次': total_normal_games, + '效果类型': '被克制' if counter_effect > 0 else '克制装置' + }) + + # 按克制程度排序(被克制程度最高的在前) + device_effects.sort(key=lambda x: -x['克制程度']) + device_counter_results[device_key] = { + 'name': device_name, + 'description': device_info['description'], + 'features': device_features, + 'effects': device_effects[:10] # 取前10个被克制最严重的怪物 + } + + return device_counter_results + + +def create_html_table(df, columns, title, is_combo=False, monster_relations=None): + """创建带有怪物头像的HTML表格""" + html = f"

{title}

\n\n" + + # 表头 + if is_combo: + html += "" + else: + html += "" + + for col in columns: + html += f"" + + # 如果是胜率表且有关系数据,添加额外的列 + if not is_combo and monster_relations and title == '所有怪物胜率排行榜': + html += "" + + html += "\n" + + # 表格内容 + row_number = 1 + for idx, row in df.iterrows(): + html += "" + + # 怪物图片和名称 + monster_id = None + if is_combo and 'ID1' in row and 'ID2' in row: + monster1_name = get_monster_name(row['ID1']) + monster2_name = get_monster_name(row['ID2']) + monster1_orig = get_monster_original_name(row['ID1']) + monster2_orig = get_monster_original_name(row['ID2']) + html += f"""""" + elif '怪物ID' in row: + monster_name = get_monster_name(row['怪物ID']) + monster_orig = get_monster_original_name(row['怪物ID']) + display_name = row.get('怪物', monster_name) + monster_id = row['怪物ID'] + html += f"""""" + else: + # 对于胜率表,使用索引作为怪物名称 + monster_name = idx + # 尝试从怪物数据中获取ID + monster_orig = monster_name + for mid in range(1, MONSTER_COUNT + 1): + if get_monster_name(mid) == monster_name: + monster_id = mid + monster_orig = get_monster_original_name(mid) + break + html += f"""""" + + # 数据列 + for col in columns: + value = row[col] + if isinstance(value, float): + if '率' in col or '程度' in col: + html += f"" + else: + html += f"" + else: + html += f"" + + # 如果是胜率表且有关系数据,添加关系信息 + if not is_combo and monster_relations and title == '所有怪物胜率排行榜' and monster_id: + relations = monster_relations.get(int(monster_id), {}) + + # 最佳队友 + html += "" + + # 克制关系 + html += "" + + # 被克制关系 + html += "" + + html += "\n" + row_number += 1 + + html += "
组合怪物{col}最佳队友克制被克制
+ {row_number}. + + {monster1_name}
+ + {monster2_name} +
+ {row_number}. + + {display_name} + + {row_number}. + + {monster_name} + {value:.2%}{value:.2f}{value}" + if relations and 'best_teammates' in relations and relations['best_teammates']: + teammates = [] + for teammate in relations['best_teammates']: + teammate_orig = get_monster_original_name(teammate['partner_id']) + teammates.append(f"""
+ + {teammate['partner_name']} ({teammate['lift']:.2f}x) +
""") + html += "".join(teammates) + else: + html += "暂无数据" + html += "
" + if relations and 'counters' in relations and relations['counters']: + counters = [] + for counter in relations['counters']: + counter_orig = get_monster_original_name(counter['opponent_id']) + counters.append(f"""
+ + {counter['opponent_name']} ({counter['win_rate']:.0%}) +
""") + html += "".join(counters) + else: + html += "暂无数据" + html += "
" + if relations and 'countered_by' in relations and relations['countered_by']: + countered = [] + for counter in relations['countered_by']: + counter_orig = get_monster_original_name(counter['opponent_id']) + countered.append(f"""
+ + {counter['opponent_name']} ({counter['lose_rate']:.0%}) +
""") + html += "".join(countered) + else: + html += "暂无数据" + html += "
\n" + return html + + +def create_device_counter_html(device_counter_effects): + """创建装置克制效果的HTML表格""" + html = "

装置克制效果统计

\n" + + for device_key, device_data in device_counter_effects.items(): + if not device_data['effects']: + continue + + device_name = device_data['name'] + device_description = device_data['description'] + effects = device_data['effects'] + + html += f"

{device_name}({device_description})

\n" + html += "\n" + html += "" + html += "\n" + + row_number = 1 + for effect in effects: + monster_name = effect['怪物'] + monster_orig = get_monster_original_name(effect['怪物ID']) + html += f""" + + + + + + + + \n""" + row_number += 1 + + html += "
怪物克制程度装置胜率普通胜率装置场次普通场次效果类型
+ {row_number}. + + {monster_name} + {effect['克制程度']:.2%}{effect['装置胜率']:.2%}{effect['普通胜率']:.2%}{effect['装置场次']}{effect['普通场次']}{effect['效果类型']}
\n
\n" + + return html + + +def generate_comprehensive_report(): + """生成综合统计报告""" + print("正在加载数据...") + df = load_data() + + print("正在计算怪物胜率...") + win_rates = calculate_all_monster_win_rates(df) + + print("正在分析怪物配合...") + combinations = analyze_monster_combinations(df) + + print("正在分析被克制关系...") + countered = find_countered_monsters(df) + + if FIELD_FEATURE_COUNT > 0: + print("正在分析地形效果...") + terrain_effects = analyze_terrain_effects(df) + + print("正在分析装置克制效果...") + device_counter_effects = analyze_device_counter_effects(df) + else: + print("地形特征数量为0,跳过地形分析...") + terrain_effects = pd.DataFrame() + device_counter_effects = {} + + print("正在分析个体怪物关系...") + monster_relations = analyze_individual_monster_relations(df) + + # 创建HTML报告 + total_battles = len(df) + monster_count = MONSTER_COUNT + field_count = FIELD_FEATURE_COUNT + + html = f""" + + + + + + +

明日方舟争锋频道绿藤城

+
+

数据概览:

+
    +
  • 总战斗记录:{total_battles} 场
  • +
  • 统计怪物数量:{monster_count} 种
  • +
  • 地形特征数量:{field_count} 种
  • +
+
+""" + + # 1. 所有怪物胜率 + if not win_rates.empty: + html += create_html_table(win_rates, ['胜场', '总场数', '胜率', '参战率'], '所有怪物胜率排行榜', monster_relations=monster_relations) + else: + html += "

所有怪物胜率排行榜

暂无数据

" + + # 2. 最佳配合 + if not combinations.empty: + html += create_html_table(combinations.head(20), ['提升度', '组合胜率', '出场次数'], '最佳怪物配合TOP20', is_combo=True) + else: + html += "

最佳怪物配合

暂无足够的配合数据

" + + + # 4. 地形效果 + if not terrain_effects.empty: + html += create_html_table(terrain_effects, ['地形', '地形胜率', '普通胜率', '影响程度'], '地形影响最大的怪物TOP20') + else: + html += "

地形影响

暂无足够的地形数据

" + + # 5. 装置克制效果 + if device_counter_effects: + html += create_device_counter_html(device_counter_effects) + else: + html += "

装置克制效果统计

暂无足够的装置数据

" + + timestamp = pd.Timestamp.now().strftime('%Y-%m-%d %H:%M:%S') + html += f""" +
+

报告生成时间:{timestamp}

+

注:数据基于历史战斗记录,仅供参考

+
+ + +""" + + # 保存报告 + with open('comprehensive_monster_report.html', 'w', encoding='utf-8') as f: + f.write(html) + + print("统计报告已生成:comprehensive_monster_report.html") + + # 也输出到控制台 + print("\n=== 怪物胜率TOP10 ===") + print(win_rates.head(10).to_string()) + + if not combinations.empty: + print("\n=== 最佳配合TOP10 ===") + print(combinations.head(10)[['组合', '提升度', '组合胜率', '出场次数']].to_string()) + + # 输出装置克制效果 + if device_counter_effects: + print("\n=== 装置克制效果统计 ===") + for device_key, device_data in device_counter_effects.items(): + if device_data['effects']: + print(f"\n{device_data['name']}({device_data['description']})最克制的怪物TOP5:") + for i, effect in enumerate(device_data['effects'][:5]): + print(f" {i+1}. {effect['怪物']} - 克制程度: {effect['克制程度']:.2%} ({effect['效果类型']})") + + if not terrain_effects.empty: + print("\n=== 地形影响TOP10 ===") + print(terrain_effects.head(10)[['地形', '怪物', '影响程度', '地形胜率', '普通胜率']].to_string()) + + + +if __name__ == "__main__": + generate_comprehensive_report() diff --git a/src/game/auto_fetch.py b/auto_fetch.py similarity index 74% rename from src/game/auto_fetch.py rename to auto_fetch.py index 9dc6f04..a17cf02 100644 --- a/src/game/auto_fetch.py +++ b/auto_fetch.py @@ -1,7 +1,6 @@ import os - # 设置 OpenCV 日志级别为 ERROR,减少 libpng 警告 -os.environ["OPENCV_LOG_LEVEL"] = "ERROR" +os.environ['OPENCV_LOG_LEVEL'] = 'ERROR' import csv import datetime @@ -10,19 +9,19 @@ from pathlib import Path import threading import time +from typing import Literal import cv2 import numpy as np -from src.recognition.recognize import INTELLIGENT_WORKERS_DEBUG -from src.core.config import MONSTER_COUNT, FIELD_FEATURE_COUNT -from src.core.paths import PROJECT_ROOT, process_image_path, simulation_path +import loadData +from recognize import intelligent_workers_debug +from config import MONSTER_COUNT, FIELD_FEATURE_COUNT from collections.abc import Callable from collections import deque -from .login import LoginManager +from login import LoginManager logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) - class GameState(Enum): MAIN_MENU = auto() MODE_SELECTION_UNSELECTED = auto() @@ -33,7 +32,6 @@ class GameState(Enum): FINISHED = auto() UNKNOWN = auto() - class AutoFetch: def __init__( self, @@ -49,7 +47,7 @@ def __init__( recognizer=None, cannot_model=None, field_recognizer=None, - start_timestamp=None, + start_timestamp=None, ): self.connector = connector self.game_mode = game_mode # 游戏模式(30人或自娱自乐) @@ -68,14 +66,10 @@ def __init__( self.monster_image = None # 当前轮次怪物图片 self.auto_fetch_running = False # 自动获取数据的状态 self.auto_fetch_thread = None # 线程引用 - self.start_time = ( - start_timestamp if start_timestamp is not None else time.time() - ) # 使用预先确定的时间戳 + self.start_time = start_timestamp if start_timestamp is not None else time.time() # 使用预先确定的时间戳 self.training_duration = training_duration # 训练时长 - self.data_folder = PROJECT_ROOT / "data" # 数据文件夹路径 - self.image_buffer = deque( - maxlen=5 - ) # 图片缓存队列,设置队列长短来保存结算前的图片 + self.data_folder = Path(f"data") # 数据文件夹路径 + self.image_buffer = deque(maxlen=5) # 图片缓存队列,设置队列长短来保存结算前的图片 self.recognizer = recognizer # 使用传入的识别器 self.cannot_model = cannot_model # 使用传入的模型 self.last_state = GameState.UNKNOWN @@ -85,24 +79,22 @@ def __init__( # 初始化状态匹配模板,缩小匹配尺寸提高速度 self.MATCH_WIDTH = 1920 // 4 self.MATCH_HEIGHT = 1080 // 4 // 4 - + # 初始化模板 self.processed_template = [] self._init_templates() - + # 根据 FIELD_FEATURE_COUNT 决定是否启用场地识别器(使用传入的实例) if FIELD_FEATURE_COUNT > 0: if self.field_recognizer is not None: logger.info(f"场地识别已启用,特征数量: {FIELD_FEATURE_COUNT}") else: - logger.warning( - f"FIELD_FEATURE_COUNT={FIELD_FEATURE_COUNT} > 0 但未传入 field_recognizer,场地识别将被禁用" - ) + logger.warning(f"FIELD_FEATURE_COUNT={FIELD_FEATURE_COUNT} > 0 但未传入 field_recognizer,场地识别将被禁用") self.field_recognizer = None else: self.field_recognizer = None logger.info("场地识别已禁用,仅收集怪物数据") - + def _log(self, level, message): """生成带有设备序列号的日志消息""" serial = getattr(self.connector, "device_serial", None) @@ -113,14 +105,10 @@ def _log(self, level, message): def _init_templates(self): for i in range(16): - img = cv2.imread(str(process_image_path(i))) + img = cv2.imread(f"images/process/{i}.png") if img is not None: # 使用最近邻插值缩放模板,速度最快 - img_resized = cv2.resize( - img, - (self.MATCH_WIDTH, self.MATCH_HEIGHT * 4), - interpolation=cv2.INTER_NEAREST, - ) + img_resized = cv2.resize(img, (self.MATCH_WIDTH, self.MATCH_HEIGHT * 4), interpolation=cv2.INTER_NEAREST) img_quarter = img_resized[self.MATCH_HEIGHT * 3 :, :] self.processed_template.append(img_quarter) else: @@ -131,31 +119,18 @@ def match_images(self, screenshot): # 裁剪底部 1/4 ROI y_start = int(h * 3 / 4) screenshot_quarter = screenshot[y_start:, :] - screenshot_quarter = cv2.resize( - screenshot_quarter, - (self.MATCH_WIDTH, self.MATCH_HEIGHT), - interpolation=cv2.INTER_NEAREST, - ) - + screenshot_quarter = cv2.resize(screenshot_quarter, (self.MATCH_WIDTH, self.MATCH_HEIGHT), interpolation=cv2.INTER_NEAREST) + results = [] for idx, template in enumerate(self.processed_template): if template is None: continue - res = cv2.matchTemplate( - screenshot_quarter, template, cv2.TM_CCOEFF_NORMED - ) + res = cv2.matchTemplate(screenshot_quarter, template, cv2.TM_CCOEFF_NORMED) _, max_val, _, _ = cv2.minMaxLoc(res) results.append((idx, max_val)) return results - def fill_data( - self, - battle_result, - recognize_results, - monster_image, - result_image, - field_recognize_result, - ): + def fill_data(self, battle_result, recognize_results, monster_image, result_image, field_recognize_result): # 获取队列头的图片 if self.image_buffer: _, previous_image, _ = self.image_buffer[0] # 获取队列头的图片 @@ -167,9 +142,7 @@ def fill_data( logger.error("未找到2秒前的图片,无法保存") return - image_name = self.get_image_name( - recognize_results, battle_result - ) # 生成图片名称 + image_name = self.get_image_name(recognize_results, battle_result) # 生成图片名称 # 确保images文件夹存在 images_folder = self.data_folder / "images" @@ -179,20 +152,12 @@ def fill_data( except Exception as e: logger.error(f"创建images文件夹失败: {e}") - if ( - INTELLIGENT_WORKERS_DEBUG - ): # 如果处于debug模式,保存人工审核图片到本地 + if intelligent_workers_debug: # 如果处于debug模式,保存人工审核图片到本地 if monster_image is not None: try: - resized_monster_img = cv2.resize( - monster_image, (960, 540) - ) # 调整分辨率为 960x540 + resized_monster_img = cv2.resize(monster_image, (960, 540)) # 调整分辨率为 960x540 image_path = images_folder / (image_name + ".jpg") - cv2.imwrite( - image_path, - resized_monster_img, - [int(cv2.IMWRITE_JPEG_QUALITY), 80], - ) + cv2.imwrite(image_path, resized_monster_img, [int(cv2.IMWRITE_JPEG_QUALITY), 80]) # logger.info(f"保存怪物图片到 {image_path}") except Exception as e: logger.error(f"保存怪物图片失败: {e}") @@ -202,17 +167,15 @@ def fill_data( try: result_image_name = image_name + "_result.jpg" # 缩放到128像素高度 - h, w = result_image.shape[:2] + (h, w) = result_image.shape[:2] new_height = 128 - resized_image = cv2.resize( - result_image, (int(w * (new_height / h)), new_height) - ) + resized_image = cv2.resize(result_image, (int(w * (new_height / h)), new_height)) image_path = images_folder / result_image_name cv2.imwrite(image_path, resized_image) logger.info(f"保存结果图片到 {image_path}") except Exception as e: logger.error(f"保存结果图片失败: {e}") - + # 原始怪物数据 left_monster_data = np.zeros(MONSTER_COUNT) right_monster_data = np.zeros(MONSTER_COUNT) @@ -238,8 +201,8 @@ def fill_data( field_feature_columns = self.field_recognizer.get_feature_columns() field_data_values = [] for col in field_feature_columns: - if col in field_recognize_result: - field_data_values.append(field_recognize_result[col]) + if col in field_recoginze_result: + field_data_values.append(field_recoginze_result[col]) else: field_data_values.append(0) # 默认值 @@ -260,9 +223,9 @@ def fill_data( logger.info("仅收集怪物数据,跳过场地特征") data_row.extend(left_monster_data.tolist()) # 左侧怪物数据 data_row.extend(right_monster_data.tolist()) # 右侧怪物数据 - + data_row.append(battle_result) # Result - + # 替换所有NaN为-1 for i, x in enumerate(data_row): if isinstance(x, (int, float)) and np.isnan(x): @@ -273,9 +236,7 @@ def fill_data( r"%Y_%m_%d__%H_%M_%S" ) - if ( - INTELLIGENT_WORKERS_DEBUG - ): # 如果处于debug模式,保存人工审核图片到本地 + if intelligent_workers_debug: # 如果处于debug模式,保存人工审核图片到本地 data_row.append(image_name) with open(self.data_folder / "arknights.csv", "a", newline="") as file: @@ -288,26 +249,24 @@ def build_terrain_features(self, left_counts, right_counts): # 获取场地特征列数 field_feature_columns = self.field_recognizer.get_feature_columns() num_field_features = len(field_feature_columns) - + # 构建地形特征向量(基于当前场地识别结果) terrain_features = np.zeros(num_field_features) - + if self.field_recognize_result: # 将场地识别结果转换为特征向量 for i, col in enumerate(field_feature_columns): if col in self.field_recognize_result: terrain_features[i] = self.field_recognize_result[col] - + # 按照data_cleaning_with_field_recognize.py的格式组织数据 - full_features = np.concatenate( - [ - left_counts, # 1L-77L - terrain_features, # 78L-83L - right_counts, # 1R-77R - terrain_features, # 78R-83R - ] - ) - + full_features = np.concatenate([ + left_counts, # 1L-77L + terrain_features, # 78L-83L + right_counts, # 1R-77R + terrain_features # 78R-83R + ]) + return full_features @staticmethod @@ -318,9 +277,7 @@ def get_saturation(bgr): cmax = max(r, g, b) cmin = min(r, g, b) delta = cmax - cmin - return ( - (delta / cmax) * 255 if cmax != 0 else 0 - ) # 返回0-255范围的饱和度值 + return (delta / cmax) * 255 if cmax != 0 else 0 # 返回0-255范围的饱和度值 if image is None: logger.error("图像加载失败") @@ -335,14 +292,14 @@ def get_saturation(bgr): (0.9, 0.1), # 右上区域 (0.9, 0.5), # 右中区域 ] - + sample_size = 20 # 增大采样区域 left_saturations = [] right_saturations = [] - + for y_ratio, x_ratio in sample_points: y_offset = int(height * y_ratio) - + # 左侧采样点 x_left_offset = int(width * 0.1) y_end = min(y_offset + sample_size, height) @@ -351,28 +308,26 @@ def get_saturation(bgr): left_region = image[y_offset:y_end, x_left_offset:x_left_end] left_mean = left_region.mean(axis=(0, 1)) left_saturations.append(get_saturation(left_mean)) - + # 右侧采样点 x_right_offset = int(width * 0.9 - sample_size) x_right_end = min(x_right_offset + sample_size, width) if y_end > y_offset and x_right_end > x_right_offset: - right_region = image[ - y_offset:y_end, x_right_offset:x_right_end - ] + right_region = image[y_offset:y_end, x_right_offset:x_right_end] right_mean = right_region.mean(axis=(0, 1)) right_saturations.append(get_saturation(right_mean)) - + if not left_saturations or not right_saturations: logger.error("无法获取有效的采样区域") return None - + # 计算平均饱和度 avg_sat_left = sum(left_saturations) / len(left_saturations) avg_sat_right = sum(right_saturations) / len(right_saturations) - + # 计算饱和度差值 saturation_diff = avg_sat_left - avg_sat_right - + # 使用自适应阈值,根据整体饱和度水平调整 base_threshold = 15 # 如果整体饱和度较低,降低阈值 @@ -383,51 +338,49 @@ def get_saturation(bgr): threshold = 12 else: threshold = base_threshold - + # 检查差值是否符合要求 if abs(saturation_diff) <= threshold: - logger.warning( - f"饱和度差值不足 (左:{avg_sat_left:.1f} vs 右:{avg_sat_right:.1f}, 阈值:{threshold})" - ) + logger.warning(f"饱和度差值不足 (左:{avg_sat_left:.1f} vs 右:{avg_sat_right:.1f}, 阈值:{threshold})") # 尝试使用亮度差异作为备选方案 return AutoFetch.calculate_brightness_diff(image) # 返回左侧是否比右侧饱和度更高 return saturation_diff > 0 - + @staticmethod def calculate_brightness_diff(image): """使用亮度差异作为胜负识别的备选方案""" if image is None: return None - + height, width, _ = image.shape - + # 转换为灰度图 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) - + # 定义左侧和右侧区域 - left_region = gray[:, : width // 2] - right_region = gray[:, width // 2 :] - + left_region = gray[:, :width//2] + right_region = gray[:, width//2:] + # 计算平均亮度 left_brightness = left_region.mean() right_brightness = right_region.mean() - + # 计算亮度差值 brightness_diff = left_brightness - right_brightness - + # 使用亮度阈值 brightness_threshold = 10 if abs(brightness_diff) <= brightness_threshold: - logger.warning( - f"亮度差值不足 (左:{left_brightness:.1f} vs 右:{right_brightness:.1f})" - ) + logger.warning(f"亮度差值不足 (左:{left_brightness:.1f} vs 右:{right_brightness:.1f})") return None - + # 返回左侧是否比右侧亮 return brightness_diff > 0 + + @staticmethod def get_image_name(recognize_results, battle_result=None): # 处理结果 @@ -457,37 +410,23 @@ def save_statistics_to_log(self): with open("log.txt", "a", encoding="utf-8") as log_file: log_file.write(stats_text) - def recognize_and_predict(self, screenshot=None): + def recognize_and_predict(self, screenshot = None): if screenshot is None: screenshot = self.connector.capture_screenshot() self.recognize_results = self.recognizer.process_regions(screenshot) - + # 场地识别 if self.field_recognizer is not None: - self.field_recognize_result = ( - self.field_recognizer.recognize_field_elements(screenshot) - ) - + self.field_recognize_result = self.field_recognizer.recognize_field_elements(screenshot) + # 输出场地识别结果日志 if self.field_recognize_result: - detected_elements = [ - key - for key, value in self.field_recognize_result.items() - if value == 1 - ] - partial_detected = [ - key - for key, value in self.field_recognize_result.items() - if value == -1 - ] + detected_elements = [key for key, value in self.field_recognize_result.items() if value == 1] + partial_detected = [key for key, value in self.field_recognize_result.items() if value == -1] if detected_elements: - logger.info( - f"场地识别检测到元素: {', '.join(detected_elements)}" - ) + logger.info(f"场地识别检测到元素: {', '.join(detected_elements)}") if partial_detected: - logger.info( - f"场地识别部分检测到元素: {', '.join(partial_detected)}" - ) + logger.info(f"场地识别部分检测到元素: {', '.join(partial_detected)}") if not detected_elements and not partial_detected: logger.info("场地识别: 未检测到任何特殊元素") else: @@ -496,73 +435,57 @@ def recognize_and_predict(self, screenshot=None): # 场地识别被禁用,设置为空结果 self.field_recognize_result = {} logger.debug("场地识别已禁用,跳过场地识别") - + # 获取预测结果 self.update_monster_callback(self.recognize_results) left_counts = np.zeros(MONSTER_COUNT, dtype=np.int16) right_counts = np.zeros(MONSTER_COUNT, dtype=np.int16) for res in self.recognize_results: - if "error" not in res: - region_id = res["region_id"] - matched_id = res["matched_id"] - number = res["number"] + if 'error' not in res: + region_id = res['region_id'] + matched_id = res['matched_id'] + number = res['number'] if matched_id == 0: continue if region_id < 3: - left_counts[matched_id - 1] = number + left_counts[matched_id -1] = number else: - right_counts[matched_id - 1] = number + right_counts[matched_id -1] = number else: logger.error("识别结果有错误,本轮跳过") # 选择预测方法 if self.cannot_model.is_model_loaded: if self.field_recognizer is not None: # 构建包含地形的完整特征向量 - full_features = self.build_terrain_features( - left_counts, right_counts - ) - self.current_prediction = ( - self.cannot_model.get_prediction_with_terrain( - full_features - ) - ) + full_features = self.build_terrain_features(left_counts, right_counts) + self.current_prediction = self.cannot_model.get_prediction_with_terrain(full_features) else: # 仅使用怪物数据进行预测 - self.current_prediction = self.cannot_model.get_prediction( - left_counts, right_counts - ) + self.current_prediction = self.cannot_model.get_prediction(left_counts, right_counts) self.update_prediction_callback(self.current_prediction) else: logger.warning("模型未加载,无法进行预测") # 人工审核保存测试用截图 - if INTELLIGENT_WORKERS_DEBUG: # 如果处于debug模式且处于自动模式 - self.monster_image = screenshot + if intelligent_workers_debug: # 如果处于debug模式且处于自动模式 + self.monster_image=screenshot def battle_result(self, result_image): result = self.calculate_average_yellow(result_image) if result is None: logger.warning("战斗结果识别失败,需要重试") return False - + if result: self.fill_data( - "L", - self.recognize_results, - self.monster_image, - result_image, - self.field_recognize_result, + "L", self.recognize_results, self.monster_image, result_image, self.field_recognize_result ) if self.current_prediction > 0.5: self.incorrect_fill_count += 1 self._log(logging.INFO, "填写数据左赢") else: self.fill_data( - "R", - self.recognize_results, - self.monster_image, - result_image, - self.field_recognize_result, + "R", self.recognize_results, self.monster_image, result_image, self.field_recognize_result ) if self.current_prediction < 0.5: self.incorrect_fill_count += 1 @@ -572,6 +495,7 @@ def battle_result(self, result_image): self._log(logging.INFO, "下一轮") return True + def auto_fetch_data(self): relative_points = [ (0.9297, 0.8833), # 右ALL、返回主页、加入赛事、开始游戏 @@ -584,14 +508,12 @@ def auto_fetch_data(self): screenshot = self.connector.capture_screenshot() if screenshot is None: self._log(logging.ERROR, "截图失败,尝试自动登录") - + # 使用 LoginManager 的自动登录(带重启重试) - if not self.login_manager.auto_login_with_restart( - lambda: self.auto_fetch_running - ): + if not self.login_manager.auto_login_with_restart(lambda: self.auto_fetch_running): self._log(logging.ERROR, "自动登录失败,无法继续操作") return - + # 检查是否已经收到停止信号 if not self.auto_fetch_running: self._log(logging.INFO, "检测到停止信号,取消后续操作") @@ -602,9 +524,7 @@ def auto_fetch_data(self): self._log(logging.INFO, "重新获取截图") screenshot = self.connector.capture_screenshot() if screenshot is None: - self._log( - logging.ERROR, "登录后仍然无法获取截图,无法继续操作" - ) + self._log(logging.ERROR, "登录后仍然无法获取截图,无法继续操作") return # 保存当前截图及其信息到缓冲区 @@ -638,9 +558,7 @@ def auto_fetch_data(self): elif best_idx in [12, 13]: current_state = GameState.FINISHED if self.last_state != current_state: - logger.info( - f"匹配到状态: {self.last_state.name} -> {current_state.name}, score:{best_score:.4f}" - ) + logger.info(f"匹配到状态: {self.last_state.name} -> {current_state.name}, score:{best_score:.4f}") else: # logger.info(f"状态机匹配置信度过低: idx:{best_idx}, score:{best_score:.4f}") pass @@ -650,68 +568,38 @@ def auto_fetch_data(self): old_state = self.last_state self.last_state = current_state elapsed = time.time() - self.state_start_time - + # 不记录 PRE_BATTLE -> IN_BATTLE 的状态变化 - if not ( - old_state == GameState.PRE_BATTLE - and current_state == GameState.IN_BATTLE - ): - self._log( - logging.INFO, - f"游戏状态变化: {old_state.name} -> {current_state.name}, 持续时间: {elapsed:.2f} 秒", - ) - + if not (old_state == GameState.PRE_BATTLE and current_state == GameState.IN_BATTLE): + self._log(logging.INFO, f"游戏状态变化: {old_state.name} -> {current_state.name}, 持续时间: {elapsed:.2f} 秒") + # 如果成功进入稳定状态且重启计数器非零,重置重启计数器 - _stable_states = { - GameState.MAIN_MENU, - GameState.IN_BATTLE, - GameState.SETTLEMENT, - GameState.FINISHED, - } - if ( - current_state in _stable_states - and self.login_manager.restart_count > 0 - ): + _stable_states = {GameState.MAIN_MENU, GameState.IN_BATTLE, GameState.SETTLEMENT, GameState.FINISHED} + if current_state in _stable_states and self.login_manager.restart_count > 0: self.login_manager.reset_restart_count() - + self.state_start_time = time.time() # 重置状态开始时间 - + # 全局超时检测:无论什么状态,只要超过时间都可能触发重启 # 非战斗状态:超过 50 秒触发重启 # 战斗流程状态:超过 120 秒触发重启(防止战斗卡死) elapsed_time = time.time() - self.state_start_time - is_battle_state = self.last_state in [ - GameState.PRE_BATTLE, - GameState.IN_BATTLE, - GameState.SETTLEMENT, - GameState.FINISHED, - ] + is_battle_state = self.last_state in [GameState.PRE_BATTLE, GameState.IN_BATTLE, GameState.SETTLEMENT, GameState.FINISHED] timeout_threshold = 120.0 if is_battle_state else 50.0 - + # 检查是否超时 if elapsed_time > timeout_threshold: state_name = self.last_state.name if self.last_state else "NONE" - self._log( - logging.WARNING, - f"在状态 '{state_name}' 停留超过 {elapsed_time:.2f} 秒(阈值: {timeout_threshold:.0f} 秒),触发重启", - ) - + self._log(logging.WARNING, f"在状态 '{state_name}' 停留超过 {elapsed_time:.2f} 秒(阈值: {timeout_threshold:.0f} 秒),触发重启") + # 使用 LoginManager 的重启登录方法 if not self.login_manager.can_restart(): - self._log( - logging.ERROR, - f"已达到最大重启次数 {self.login_manager.max_restart_count} 次,停止运行", - ) + self._log(logging.ERROR, f"已达到最大重启次数 {self.login_manager.max_restart_count} 次,停止运行") self.auto_fetch_running = False self.stop_callback() - elif not self.login_manager.restart_and_login( - first_start=False, - stop_callback=lambda: self.auto_fetch_running, - ): - self._log( - logging.WARNING, "本次重启登录失败,将在下次超时后重试" - ) - + elif not self.login_manager.restart_and_login(first_start=False, stop_callback=lambda: self.auto_fetch_running): + self._log(logging.WARNING, "本次重启登录失败,将在下次超时后重试") + # 检测完毕后,无论结果如何,重置计时器,避免频繁阻塞 self.state_start_time = time.time() self._log(logging.INFO, "重置状态计时器") @@ -769,9 +657,7 @@ def auto_fetch_data(self): if not self._sleep_with_check(3): return if self.game_mode == "30人": - self._log( - logging.INFO, "30人模式下,投资后需要等待20秒" - ) + self._log(logging.INFO, "30人模式下,投资后需要等待20秒") if not self._sleep_with_check(5): return else: # 不投资 @@ -786,9 +672,7 @@ def auto_fetch_data(self): case GameState.SETTLEMENT: if not self.battle_result(screenshot): new_screenshot = self.connector.capture_screenshot() - if new_screenshot is not None and not self.battle_result( - new_screenshot - ): + if new_screenshot is not None and not self.battle_result(new_screenshot): self._log(logging.ERROR, "战斗结果识别失败,跳过本轮") if not self._sleep_with_check(5): return @@ -806,33 +690,26 @@ def auto_fetch_loop(self): # 每次循环开始时检查状态 if not self.auto_fetch_running: break - - # 刷新当前预测显示(心跳):不要写入固定0,避免把GUI错误覆盖成“左方100%” - self.update_prediction_callback(self.current_prediction) - + + self.updater() # 多开器如果不更新会强制停止 self.auto_fetch_data() # 每次循环结束时检查状态 if not self.auto_fetch_running: break - + elapsed_time = time.time() - self.start_time - if ( - self.training_duration != -1 - and elapsed_time >= self.training_duration - ): + if self.training_duration != -1 and elapsed_time >= self.training_duration: self._log(logging.INFO, "已达到设定时长,结束自动获取") break # 检测一次间隔时间—————————————————————————————————— - time.sleep(0.1) + time.sleep(0.2) except Exception as e: self._log(logging.ERROR, f"自动获取数据出错:\n{e}") break else: - self._log( - logging.INFO, "auto_fetch_running is False, exiting loop" - ) + self._log(logging.INFO, "auto_fetch_running is False, exiting loop") return # 不通过按钮结束自动获取 self._log(logging.INFO, "break auto_fetch_loop") @@ -842,57 +719,33 @@ def start_auto_fetch(self): if not self.auto_fetch_running: self.auto_fetch_running = True # 使用初始化时设置的时间戳,不重新获取当前时间 - start_time = datetime.datetime.fromtimestamp( - self.start_time - ).strftime(r"%Y_%m_%d__%H_%M_%S") - self.data_folder = PROJECT_ROOT / "data" / start_time + start_time = datetime.datetime.fromtimestamp(self.start_time).strftime( + r"%Y_%m_%d__%H_%M_%S" + ) + self.data_folder = Path(f"data/{start_time}") self._log(logging.INFO, f"创建文件夹: {self.data_folder}") self.data_folder.mkdir(parents=True, exist_ok=True) # 创建文件夹 (self.data_folder / "images").mkdir(parents=True, exist_ok=True) - with open( - self.data_folder / "arknights.csv", "w", newline="" - ) as file: + with open(self.data_folder / "arknights.csv", "w", newline="") as file: # 创建CSV表头 if self.field_recognizer is not None: # 获取场地特征列数 - num_field_features = len( - self.field_recognizer.get_feature_columns() - ) - + num_field_features = len(self.field_recognizer.get_feature_columns()) + # 按照data_cleaning_with_field_recognize_gpu.py的格式创建表头 - header = [ - f"{i+1}L" for i in range(MONSTER_COUNT) - ] # 1L-77L - header += [ - f"{i+1}LF" - for i in range( - MONSTER_COUNT, MONSTER_COUNT + num_field_features - ) - ] # 78LF-83LF (场地特征) - header += [ - f"{i+1}R" for i in range(MONSTER_COUNT) - ] # 1R-77R - header += [ - f"{i+1}RF" - for i in range( - MONSTER_COUNT, MONSTER_COUNT + num_field_features - ) - ] # 78RF-83RF (场地特征) + header = [f"{i+1}L" for i in range(MONSTER_COUNT)] # 1L-77L + header += [f"{i+1}LF" for i in range(MONSTER_COUNT, MONSTER_COUNT + num_field_features)] # 78LF-83LF (场地特征) + header += [f"{i+1}R" for i in range(MONSTER_COUNT)] # 1R-77R + header += [f"{i+1}RF" for i in range(MONSTER_COUNT, MONSTER_COUNT + num_field_features)] # 78RF-83RF (场地特征) header += ["Result", "ImgPath"] - logger.info( - f"创建包含场地特征的CSV表头,场地特征数: {num_field_features}" - ) + logger.info(f"创建包含场地特征的CSV表头,场地特征数: {num_field_features}") else: # 仅怪物数据的格式 - header = [ - f"{i+1}L" for i in range(MONSTER_COUNT) - ] # 左侧怪物数据 - header += [ - f"{i+1}R" for i in range(MONSTER_COUNT) - ] # 右侧怪物数据 + header = [f"{i+1}L" for i in range(MONSTER_COUNT)] # 左侧怪物数据 + header += [f"{i+1}R" for i in range(MONSTER_COUNT)] # 右侧怪物数据 header += ["Result", "ImgPath"] logger.info("创建仅包含怪物数据的CSV表头") - + writer = csv.writer(file) writer.writerow(header) self.log_file_handler = logging.FileHandler( @@ -904,11 +757,9 @@ def start_auto_fetch(self): self.log_file_handler.setFormatter(file_formatter) self.log_file_handler.setLevel(logging.INFO) logger.addHandler(self.log_file_handler) - + # 启动自动获取数据线程 - self.auto_fetch_thread = threading.Thread( - target=self.auto_fetch_loop - ) + self.auto_fetch_thread = threading.Thread(target=self.auto_fetch_loop) self.auto_fetch_thread.start() logger.info("自动获取数据已启动") self.start_callback() @@ -923,14 +774,14 @@ def _sleep_with_check(self, seconds): return False time.sleep(0.1) return True - + def stop_auto_fetch(self): if not self.auto_fetch_running: return # 强制设置停止标志,不等待线程退出 self.auto_fetch_running = False self._log(logging.INFO, "强制停止自动获取") - + # 不等待线程退出,让线程在下一次循环时自己检测到停止标志 self.save_statistics_to_log() self.stop_callback() diff --git a/src/core/config.py b/config.py similarity index 54% rename from src/core/config.py rename to config.py index 819cea0..31acae5 100644 --- a/src/core/config.py +++ b/config.py @@ -1,14 +1,13 @@ +from pathlib import Path import cv2 import numpy as np import pandas as pd import logging -from .paths import DATA_DIR, IMAGES_DIR - logger = logging.getLogger(__name__) -FIELD_FEATURE_COUNT = 0 - +# 全局地形特征数量常量 +FIELD_FEATURE_COUNT = 0 # 默认值 def load_images() -> dict[str, np.ndarray]: """ @@ -16,13 +15,13 @@ def load_images() -> dict[str, np.ndarray]: returns: dict - 图片字典,键为文件名(不含扩展名),值为numpy.ndarray对象 """ images = {} - images_path = IMAGES_DIR - for image_file in images_path.glob("*.*"): - if image_file.suffix.lower() in (".png", ".jpg", ".jpeg", ".bmp"): + images_path = Path('images') + # 遍历images目录下的所有文件 + for image_file in images_path.glob('*.*'): + if image_file.suffix.lower() in ('.png', '.jpg', '.jpeg', '.bmp'): try: - img = cv2.imdecode( - np.fromfile(image_file, dtype=np.uint8), cv2.IMREAD_COLOR - ) + # img = cv2.imread(str(image_file), cv2.IMREAD_COLOR) + img = cv2.imdecode(np.fromfile(image_file, dtype=np.uint8), cv2.IMREAD_COLOR) if img is None: logger.error(f"无法加载图片: {image_file}") continue @@ -31,19 +30,13 @@ def load_images() -> dict[str, np.ndarray]: logger.error(f"加载图片出错 {image_file}: {str(e)}") return images - MONSTER_IMAGES = load_images() - def load_monster_data(): - monster_data = pd.read_csv( - DATA_DIR / "monster_greenvine.csv", - index_col="id", - encoding="utf-8-sig", - ) + monster_data = pd.read_csv('monster_greenvine.csv', index_col="id", encoding='utf-8-sig') return monster_data - MONSTER_DATA = load_monster_data() -MONSTER_COUNT = len(MONSTER_DATA) +# 全局变量 +MONSTER_COUNT = len(MONSTER_DATA) # 根据怪物数据自动设置数量 diff --git a/src/core/constants.py b/constants.py similarity index 89% rename from src/core/constants.py rename to constants.py index a1a2590..60d3adc 100644 --- a/src/core/constants.py +++ b/constants.py @@ -7,10 +7,10 @@ "health": 1390, "magic_resist": 0, "attack_interval": 3.3, - "move_speed": 1 / 2, # 除2是因为方舟就是要除2 + "move_speed": 1 / 2, # 除2是因为方舟就是要除2 "attack_radius": 2.75, "effect": "破甲15", - "icon": "", + "icon": "images/1.png" }, 2: { "name": "灼热源石虫", @@ -23,7 +23,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "", + "icon": "images/2.png" }, 3: { "name": "狂暴的猎狗pro", @@ -36,7 +36,7 @@ "move_speed": 1.9 / 2, "attack_radius": 0.8, "effect": "", - "icon": "", + "icon": "images/3.png" }, 4: { "name": "炮击组长", @@ -49,7 +49,7 @@ "move_speed": 0.8 / 2, "attack_radius": 7, "effect": "溅射", - "icon": "", + "icon": "images/4.png" }, 5: { "name": "萨卡兹大剑手", @@ -62,7 +62,7 @@ "move_speed": 0.85 / 2, "attack_radius": 0.8, "effect": "", - "icon": "", + "icon": "images/5.png" }, 6: { "name": "宿主流浪者", @@ -75,7 +75,7 @@ "move_speed": 0.65 / 2, "attack_radius": 0.8, "effect": "再生", - "icon": "", + "icon": "images/6.png" }, 7: { "name": "重装防御者", @@ -88,7 +88,7 @@ "move_speed": 0.75 / 2, "attack_radius": 0.8, "effect": "", - "icon": "", + "icon": "images/7.png" }, 8: { "name": "复仇者", @@ -101,7 +101,7 @@ "move_speed": 0.65 / 2, "attack_radius": 0.8, "effect": "火刀", - "icon": "", + "icon": "images/8.png" }, 9: { "name": "狂暴宿主组长", @@ -114,7 +114,7 @@ "move_speed": 1.2 / 2, "attack_radius": 0.8, "effect": "掉血", - "icon": "", + "icon": "images/9.png" }, 10: { "name": "巧克力流心虫虫", @@ -127,7 +127,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "", + "icon": "images/2.png" }, 11: { "name": "巧克力流心虫虫", @@ -140,7 +140,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "", + "icon": "images/2.png" }, 12: { "name": "泥岩巨像", @@ -153,7 +153,7 @@ "move_speed": 0.4 / 2, "attack_radius": 0.8, "effect": "", - "icon": "", + "icon": "images/12.png" }, 13: { "name": "高塔术师", @@ -166,7 +166,7 @@ "move_speed": 0.6 / 2, "attack_radius": 3.2, "effect": "打2 溅射", - "icon": "", + "icon": "images/13.png" }, 14: { "name": "巧克力流心虫虫", @@ -179,7 +179,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "", + "icon": "images/2.png" }, 15: { "name": "巧克力流心虫虫", @@ -192,7 +192,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "", + "icon": "images/2.png" }, 16: { "name": "冰原术师", @@ -205,7 +205,7 @@ "move_speed": 0.7 / 2, "attack_radius": 3.2, "effect": "打2 寒冷", - "icon": "", + "icon": "images/16.png" }, 17: { "name": "巧克力流心虫虫", @@ -218,7 +218,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "", + "icon": "images/2.png" }, 18: { "name": "弧光锋卫长", @@ -231,7 +231,7 @@ "move_speed": 0.75 / 2, "attack_radius": 0.8, "effect": "", - "icon": "", + "icon": "images/18.png" }, 19: { "name": "巧克力流心虫虫", @@ -244,7 +244,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "", + "icon": "images/2.png" }, 20: { "name": "提亚卡乌好战者", @@ -257,7 +257,7 @@ "move_speed": 0.8 / 2, "attack_radius": 0.8, "effect": "破甲10", - "icon": "", + "icon": "images/20.png" }, 21: { "name": "矿脉守卫", @@ -270,7 +270,7 @@ "move_speed": 0.6 / 2, "attack_radius": 0.8, "effect": "反伤", - "icon": "", + "icon": "images/21.png" }, 22: { "name": "巧克力流心虫虫", @@ -283,7 +283,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "", + "icon": "images/2.png" }, 23: { "name": "巧克力流心虫虫", @@ -296,7 +296,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "", + "icon": "images/2.png" }, 24: { "name": "巧克力流心虫虫", @@ -309,7 +309,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "", + "icon": "images/2.png" }, 25: { "name": "巧克力流心虫虫", @@ -322,7 +322,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "", + "icon": "images/2.png" }, 26: { "name": "巧克力流心虫虫", @@ -335,6 +335,6 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "", - }, -} + "icon": "images/2.png" + } +} \ No newline at end of file diff --git a/src/ui/dark_mode_style_fix.py b/dark_mode_style_fix.py similarity index 100% rename from src/ui/dark_mode_style_fix.py rename to dark_mode_style_fix.py diff --git a/src/data/data_package.py b/data_package.py similarity index 96% rename from src/data/data_package.py rename to data_package.py index 79fe590..dbc90b5 100644 --- a/src/data/data_package.py +++ b/data_package.py @@ -36,11 +36,10 @@ def create_zip_package(output_zip_path): shutil.rmtree(folder) print(f"已删除文件夹:{folder}") except Exception as e: - print(f"删除文件夹 {folder} 时出错:{e}") + print(f"删除文件夹 {folder} 时出错:{e}") print(f"压缩包已创建:{output_zip_path}") - def package_data(): # 使用当前时间生成输出文件名 current_time = datetime.now().strftime("%Y_%m_%d__%H_%M_%S") @@ -50,6 +49,5 @@ def package_data(): create_zip_package(output_zip) return output_zip - if __name__ == "__main__": package_data() diff --git a/src/__init__.py "b/data_train/package/\346\225\260\346\215\256\345\214\205\346\224\276\345\234\250\346\255\244\350\267\257\345\276\204.md" similarity index 100% rename from src/__init__.py rename to "data_train/package/\346\225\260\346\215\256\345\214\205\346\224\276\345\234\250\346\255\244\350\267\257\345\276\204.md" diff --git a/src/tools/merge_data.py "b/data_train/\346\225\260\346\215\256\345\220\210\345\271\266.py" similarity index 69% rename from src/tools/merge_data.py rename to "data_train/\346\225\260\346\215\256\345\220\210\345\271\266.py" index 879b10b..4c31aa0 100644 --- a/src/tools/merge_data.py +++ "b/data_train/\346\225\260\346\215\256\345\220\210\345\271\266.py" @@ -14,16 +14,12 @@ if str(project_root) not in sys.path: sys.path.insert(0, str(project_root)) -# from config import MONSTER_COUNT, FIELD_FEATURE_COUNT config会读取图片导致非常慢 - +#from config import MONSTER_COUNT, FIELD_FEATURE_COUNT config会读取图片导致非常慢 def load_monster_data(): - monster_data = pd.read_csv( - "monster_greenvine.csv", index_col="id", encoding="utf-8-sig" - ) + monster_data = pd.read_csv('monster_greenvine.csv', index_col="id", encoding='utf-8-sig') return monster_data - MONSTER_DATA = load_monster_data() # 全局变量 @@ -35,15 +31,9 @@ def get_expected_header(): """根据配置生成预期表头""" if FIELD_FEATURE_COUNT > 0: header = [f"{i + 1}L" for i in range(MONSTER_COUNT)] - header += [ - f"{i + 1}LF" - for i in range(MONSTER_COUNT, MONSTER_COUNT + FIELD_FEATURE_COUNT) - ] + header += [f"{i + 1}LF" for i in range(MONSTER_COUNT, MONSTER_COUNT + FIELD_FEATURE_COUNT)] header += [f"{i + 1}R" for i in range(MONSTER_COUNT)] - header += [ - f"{i + 1}RF" - for i in range(MONSTER_COUNT, MONSTER_COUNT + FIELD_FEATURE_COUNT) - ] + header += [f"{i + 1}RF" for i in range(MONSTER_COUNT, MONSTER_COUNT + FIELD_FEATURE_COUNT)] header += ["Result", "ImgPath"] else: header = [f"{i + 1}L" for i in range(MONSTER_COUNT)] @@ -54,12 +44,12 @@ def get_expected_header(): def read_csv_from_zip(zip_ref, csv_filename): """从 ZIP 文件流中直接读取 CSV 数据,尝试多种编码""" - encodings = ["utf-8-sig", "utf-8", "gbk", "gb18030", "big5", "latin1"] + encodings = ['utf-8-sig', 'utf-8', 'gbk', 'gb18030', 'big5', 'latin1'] for encoding in encodings: try: with zip_ref.open(csv_filename) as f: - text_f = io.TextIOWrapper(f, encoding=encoding, newline="") + text_f = io.TextIOWrapper(f, encoding=encoding, newline='') reader = csv.reader(text_f) try: header = next(reader) @@ -75,8 +65,8 @@ def read_csv_from_zip(zip_ref, csv_filename): def process_archives(merge_images=True, extract_result_images=False): package_dir = base_dir / "package" - target_images_dir = base_dir / "images" - target_csv_path = base_dir / "arknights.csv" + target_images_dir = base_dir / 'images' + target_csv_path = base_dir / 'arknights.csv' # 1. 目录准备 (移除旧的清空逻辑,保证增量更新) if not package_dir.exists(): @@ -92,16 +82,12 @@ def process_archives(merge_images=True, extract_result_images=False): # 2. 构建现有 CSV 数据的索引集合 seen_csv_img_paths = set() - is_csv_initialized = False # 用于判断是否需要写入表头 + is_csv_initialized = False # 用于判断是否需要写入表头 if target_csv_path.exists(): - print( - f"检测到已存在的 {target_csv_path.name},正在读取历史记录构建索引..." - ) + print(f"检测到已存在的 {target_csv_path.name},正在读取历史记录构建索引...") try: - with open( - target_csv_path, "r", encoding="utf-8-sig", newline="" - ) as f: + with open(target_csv_path, 'r', encoding='utf-8-sig', newline='') as f: reader = csv.reader(f) try: header = next(reader) @@ -110,14 +96,10 @@ def process_archives(merge_images=True, extract_result_images=False): for row in reader: if len(row) > img_path_idx: seen_csv_img_paths.add(row[img_path_idx]) - print( - f"-> 成功加载 {len(seen_csv_img_paths)} 条历史记录的索引。" - ) + print(f"-> 成功加载 {len(seen_csv_img_paths)} 条历史记录的索引。") else: - print( - "-> 警告:已存在 CSV 的表头与配置不符,将创建新文件或覆写。" - ) - target_csv_path.unlink() # 表头不对则删除重建 + print("-> 警告:已存在 CSV 的表头与配置不符,将创建新文件或覆写。") + target_csv_path.unlink() # 表头不对则删除重建 except StopIteration: # 文件为空 pass @@ -126,7 +108,7 @@ def process_archives(merge_images=True, extract_result_images=False): target_csv_path.unlink(missing_ok=True) # 定义可能的文件后缀,涵盖常见大小写 - possible_extensions = [".jpg", ".png", ".jpeg", ".JPG", ".PNG", ".JPEG"] + possible_extensions = ['.jpg', '.png', '.jpeg', '.JPG', '.PNG', '.JPEG'] zip_files = list(package_dir.glob("*.zip")) print(f"\n找到 {len(zip_files)} 个压缩包,准备进行增量处理...") @@ -139,19 +121,17 @@ def process_archives(merge_images=True, extract_result_images=False): print(f"\n正在处理压缩包: {zip_path.name}") try: - with zipfile.ZipFile(zip_path, "r") as zf: + with zipfile.ZipFile(zip_path, 'r') as zf: # 建立 namelist 的 O(1) 查找集合 zip_namelist_set = set(zf.namelist()) - + # ========================================== # 任务 1:完全独立处理 CSV 文件 # ========================================== - csv_members = [ - m for m in zip_namelist_set if m.endswith("arknights.csv") - ] + csv_members = [m for m in zip_namelist_set if m.endswith('arknights.csv')] zip_added_csv_count = 0 - + for csv_member in csv_members: header, data, encoding = read_csv_from_zip(zf, csv_member) @@ -173,24 +153,17 @@ def process_archives(merge_images=True, extract_result_images=False): # 追加写入 CSV if zip_new_csv_rows: - mode = "a" if is_csv_initialized else "w" - with open( - target_csv_path, - mode, - newline="", - encoding="utf-8-sig", - ) as f: + mode = 'a' if is_csv_initialized else 'w' + with open(target_csv_path, mode, newline='', encoding='utf-8-sig') as f: writer = csv.writer(f) if not is_csv_initialized: writer.writerow(expected_header) is_csv_initialized = True writer.writerows(zip_new_csv_rows) - + zip_added_csv_count += len(zip_new_csv_rows) print(f" -> {csv_member} (编码: {encoding})") - print( - f" CSV: 新增 {len(zip_new_csv_rows)} 条,重复跳过 {skip_csv_count} 条" - ) + print(f" CSV: 新增 {len(zip_new_csv_rows)} 条,重复跳过 {skip_csv_count} 条") total_added_rows += zip_added_csv_count @@ -207,39 +180,29 @@ def process_archives(merge_images=True, extract_result_images=False): # 排除 Mac 系统压缩可能产生的隐藏文件干扰 if "__MACOSX" in member: continue - + filename = Path(member).name - is_result_img = filename.rsplit(".", 1)[ - 0 - ].endswith("_result") + is_result_img = filename.rsplit('.', 1)[0].endswith('_result') # 根据参数决定是否跳过 result 图 if is_result_img and not extract_result_images: continue target_img_path = target_images_dir / filename - + # 增量判断:如果本地没有,则解压提取 if target_img_path.exists(): zip_skip_img_count += 1 else: try: with zf.open(member) as source_file: - with open( - target_img_path, "wb" - ) as target_file: - shutil.copyfileobj( - source_file, target_file - ) + with open(target_img_path, 'wb') as target_file: + shutil.copyfileobj(source_file, target_file) zip_extracted_img_count += 1 except Exception as e: - print( - f" [错误] 提取图片 {member} 失败: {e}" - ) - - print( - f" IMG: 提取新图 {zip_extracted_img_count} 张,已存在跳过 {zip_skip_img_count} 张" - ) + print(f" [错误] 提取图片 {member} 失败: {e}") + + print(f" IMG: 提取新图 {zip_extracted_img_count} 张,已存在跳过 {zip_skip_img_count} 张") total_extracted_imgs += zip_extracted_img_count except zipfile.BadZipFile: @@ -253,9 +216,7 @@ def process_archives(merge_images=True, extract_result_images=False): print(f"总计提取新图片: {total_extracted_imgs} 张") -if __name__ == "__main__": - merge_imgs = False # 设置为 True 则提取阵容图 +if __name__ == '__main__': + merge_imgs = False # 设置为 True 则提取阵容图 extract_res_imgs = False # 设置为 True 则同时提取带有 _result 的结果图 - process_archives( - merge_images=merge_imgs, extract_result_images=extract_res_imgs - ) + process_archives(merge_images=merge_imgs, extract_result_images=extract_res_imgs) diff --git a/src/recognition/field_recognition.py b/field_recognition.py similarity index 68% rename from src/recognition/field_recognition.py rename to field_recognition.py index c179e1c..59344bb 100644 --- a/src/recognition/field_recognition.py +++ b/field_recognition.py @@ -6,6 +6,7 @@ from collections import defaultdict from PIL import Image import cv2 +import numpy as np import torch import torch.nn as nn from torchvision import models, transforms @@ -17,22 +18,22 @@ "altar_vertical": [ {"x": 910, "y": 174, "width": 95, "height": 104}, {"x": 910, "y": 429, "width": 102, "height": 108}, - {"x": 900, "y": 755, "width": 120, "height": 108}, + {"x": 900, "y": 755, "width": 120, "height": 108} ], "block_parallel": [ {"x": 694, "y": 240, "width": 530, "height": 122}, - {"x": 651, "y": 614, "width": 620, "height": 143}, + {"x": 651, "y": 614, "width": 620, "height": 143} ], "block_vertical": [ {"x": 647, "y": 233, "width": 153, "height": 523}, - {"x": 1112, "y": 239, "width": 159, "height": 514}, + {"x": 1112, "y": 239, "width": 159, "height": 514} ], "coil_narrow": [ {"x": 915, "y": 110, "width": 85, "height": 89}, {"x": 815, "y": 257, "width": 86, "height": 98}, {"x": 1024, "y": 258, "width": 79, "height": 98}, {"x": 790, "y": 643, "width": 97, "height": 102}, - {"x": 1031, "y": 639, "width": 102, "height": 108}, + {"x": 1031, "y": 639, "width": 102, "height": 108} ], "coil_wide": [ {"x": 719, "y": 181, "width": 81, "height": 89}, @@ -42,18 +43,23 @@ {"x": 1159, "y": 757, "width": 93, "height": 92}, {"x": 1257, "y": 533, "width": 94, "height": 102}, {"x": 1236, "y": 344, "width": 85, "height": 97}, - {"x": 1120, "y": 180, "width": 75, "height": 91}, + {"x": 1120, "y": 180, "width": 75, "height": 91} + ], + "crossbow_top": [ + {"x": 718, "y": 13, "width": 484, "height": 106} + ], + "fire_side_left": [ + {"x": 98, "y": 246, "width": 184, "height": 281} + ], + "fire_side_right": [ + {"x": 1656, "y": 430, "width": 235, "height": 315} ], - "crossbow_top": [{"x": 718, "y": 13, "width": 484, "height": 106}], - "fire_side_left": [{"x": 98, "y": 246, "width": 184, "height": 281}], - "fire_side_right": [{"x": 1656, "y": 430, "width": 235, "height": 315}], "fire_top": [ {"x": 532, "y": 17, "width": 188, "height": 97}, - {"x": 1325, "y": 14, "width": 60, "height": 100}, - ], + {"x": 1325, "y": 14, "width": 60, "height": 100} + ] } - class FieldRecognizer: def __init__(self): self.field_model = None @@ -63,7 +69,7 @@ def __init__(self): self.grouped_elements = {} self.image_feature_columns = [] self.is_initialized = False - + # 初始化场地识别模型 self._init_field_recognition() @@ -71,23 +77,19 @@ def _init_field_recognition(self): """初始化场地识别模型和相关组件""" try: # 设置设备 - self.field_device = torch.device( - "cuda" if torch.cuda.is_available() else "cpu" - ) + self.field_device = torch.device("cuda" if torch.cuda.is_available() else "cpu") logger.info(f"场地识别将使用设备: {self.field_device}") - + # 加载类别映射 model_dir = Path("tools/battlefield_recognize") class_map_path = model_dir / "class_to_idx.json" pth_model_path = model_dir / "field_recognize.pth" - + if not class_map_path.exists(): - logger.warning( - "找不到场地识别类别映射文件,跳过场地识别初始化" - ) + logger.warning("找不到场地识别类别映射文件,跳过场地识别初始化") return - with open(class_map_path, "r", encoding="utf-8") as f: + with open(class_map_path, 'r', encoding='utf-8') as f: class_to_idx = json.load(f) self.idx_to_class = {v: k for k, v in class_to_idx.items()} num_classes = len(class_to_idx) @@ -95,45 +97,35 @@ def _init_field_recognition(self): # 准备特征列 self.grouped_elements = defaultdict(list) for class_name in class_to_idx.keys(): - if class_name.endswith("_none"): + if class_name.endswith('_none'): continue - condensed_name = re.sub(r"_left_", "_", class_name) - condensed_name = re.sub(r"_right_", "_", condensed_name) + condensed_name = re.sub(r'_left_', '_', class_name) + condensed_name = re.sub(r'_right_', '_', condensed_name) self.grouped_elements[condensed_name].append(class_name) self.image_feature_columns = sorted(self.grouped_elements.keys()) - + if not pth_model_path.exists(): logger.warning("找不到场地识别模型文件,跳过场地识别初始化") return # 加载模型 - self.field_model = self._load_pytorch_model( - str(pth_model_path), num_classes, self.field_device - ) - + self.field_model = self._load_pytorch_model(str(pth_model_path), num_classes, self.field_device) + # 设置图像变换 - self.field_transform = transforms.Compose( - [ - transforms.Resize((224, 224)), - transforms.ToTensor(), - transforms.Normalize( - [0.485, 0.456, 0.406], [0.229, 0.224, 0.225] - ), - ] - ) - + self.field_transform = transforms.Compose([ + transforms.Resize((224, 224)), + transforms.ToTensor(), + transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) + ]) + self.is_initialized = True - logger.info( - f"场地识别初始化成功,将生成 {len(self.image_feature_columns)} 个特征列" - ) - + logger.info(f"场地识别初始化成功,将生成 {len(self.image_feature_columns)} 个特征列") + except Exception as e: logger.error(f"场地识别初始化失败: {e}") self.is_initialized = False - def _load_pytorch_model( - self, model_path: str, num_classes: int, device: torch.device - ): + def _load_pytorch_model(self, model_path: str, num_classes: int, device: torch.device): """加载 PyTorch 模型并设置为评估模式""" logger.info(f"正在加载 PyTorch 模型: {model_path}") model = models.mobilenet_v3_small(weights=None) @@ -145,12 +137,10 @@ def _load_pytorch_model( logger.info("模型加载成功并已切换到评估模式。") return model - def _predict_scene_pytorch( - self, image_path: str, threshold: float = 0.5 - ) -> list[str]: + def _predict_scene_pytorch(self, image_path: str, threshold: float = 0.5) -> list[str]: """使用 PyTorch 模型对给定图片的所有 ROI 进行分类预测""" try: - full_image = Image.open(image_path).convert("RGB") + full_image = Image.open(image_path).convert('RGB') except Exception: return [] @@ -161,27 +151,18 @@ def _predict_scene_pytorch( with torch.no_grad(): for location, boxes in ROI_COORDINATES.items(): for i, box in enumerate(boxes): - x, y, w, h = ( - box["x"], - box["y"], - box["width"], - box["height"], - ) + x, y, w, h = box['x'], box['y'], box['width'], box['height'] roi_pil = full_image.crop((x, y, x + w, y + h)) input_tensor = self.field_transform(roi_pil).unsqueeze(0) input_tensor = input_tensor.to(self.field_device) outputs = self.field_model(input_tensor) - probabilities = torch.nn.functional.softmax( - outputs, dim=1 - )[0] - max_prob, predicted_index_tensor = torch.max( - probabilities, 0 - ) + probabilities = torch.nn.functional.softmax(outputs, dim=1)[0] + max_prob, predicted_index_tensor = torch.max(probabilities, 0) predicted_index = predicted_index_tensor.item() if max_prob.item() >= threshold: predicted_class = self.idx_to_class[predicted_index] - if not predicted_class.endswith("_none"): + if not predicted_class.endswith('_none'): detected_classes.append(predicted_class) return detected_classes @@ -190,37 +171,31 @@ def recognize_field_elements(self, screenshot): if not self.is_initialized or self.field_model is None: logger.debug("场地识别模型未初始化,跳过场地识别") return {} - + try: # 将OpenCV图像转换为PIL图像 screenshot_rgb = cv2.cvtColor(screenshot, cv2.COLOR_BGR2RGB) pil_image = Image.fromarray(screenshot_rgb) - + # 保存临时文件用于识别 temp_image_path = "temp_screenshot.png" pil_image.save(temp_image_path) - + # 进行场地识别 - detected_full_names = set( - self._predict_scene_pytorch(temp_image_path, threshold=0.5) - ) - + detected_full_names = set(self._predict_scene_pytorch(temp_image_path, threshold=0.5)) + # 删除临时文件 if os.path.exists(temp_image_path): os.remove(temp_image_path) - + # 处理识别结果 field_data = {} for condensed_name, full_names in self.grouped_elements.items(): num_positions = len(full_names) if num_positions == 1: - field_data[condensed_name] = ( - 1 if full_names[0] in detected_full_names else 0 - ) + field_data[condensed_name] = 1 if full_names[0] in detected_full_names else 0 else: - detections_in_group = [ - fn in detected_full_names for fn in full_names - ] + detections_in_group = [fn in detected_full_names for fn in full_names] num_detected = sum(detections_in_group) if num_detected == num_positions: field_data[condensed_name] = 1 @@ -228,12 +203,10 @@ def recognize_field_elements(self, screenshot): field_data[condensed_name] = 0 else: field_data[condensed_name] = -1 - - logger.debug( - f"场地识别完成,检测到元素: {list(detected_full_names)}" - ) + + logger.debug(f"场地识别完成,检测到元素: {list(detected_full_names)}") return field_data - + except Exception as e: logger.error(f"场地识别失败: {e}") return {} diff --git a/src/recognition/find_monster_zone.py b/find_monster_zone.py similarity index 58% rename from src/recognition/find_monster_zone.py rename to find_monster_zone.py index b837bba..8d3b273 100644 --- a/src/recognition/find_monster_zone.py +++ b/find_monster_zone.py @@ -2,12 +2,9 @@ import cv2 import numpy as np -from src.core.paths import ensure_tmp_images_dir - logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) - def custom_least_squares(fun, x0, args=()): """ A simple linear least squares solver to replace scipy.optimize.least_squares. @@ -16,13 +13,10 @@ def custom_least_squares(fun, x0, args=()): x = np.array(x0, dtype=float) r = np.array(fun(x, *args)) if r.size == 0: - class Result: - def __init__(self, x): - self.x = x - + def __init__(self, x): self.x = x return Result(x) - + n = len(x) J = np.zeros((len(r), n)) eps = 1e-6 @@ -31,16 +25,13 @@ def __init__(self, x): x_eps[i] += eps r_eps = np.array(fun(x_eps, *args)) J[:, i] = (r_eps - r) / eps - + delta, _, _, _ = np.linalg.lstsq(J, -r, rcond=None) - + class Result: - def __init__(self, x): - self.x = x - + def __init__(self, x): self.x = x return Result(x + delta) - # 类伽马变换函数 def adjust_quasi_gamma(image): c = np.arange(256.0 / 255, step=1.0 / 255) @@ -139,9 +130,7 @@ def find_big(crop, x_ratio, minR, maxR, width, p1=30, p2=35): results.append(j) logger.debug(f"big circle: {j}") else: - logger.warning( - f"section{x_ratio.index(i)} big circle not detected" - ) + logger.warning(f"section{x_ratio.index(i)} big circle not detected") results = np.array(results) return results @@ -162,18 +151,14 @@ def find_small(crop_small, x_ratio_small, x_ratio, minR, maxR, width): ) if circles_small is not None: circles_small_bias = circles_small[0].copy() - circles_small_bias[:, 0] = circles_small[0][:, 0] + np.round( - i * width - ) + circles_small_bias[:, 0] = circles_small[0][:, 0] + np.round(i * width) for m in circles_small_bias: j = np.append(m, x_ratio.index(i)) results.append(j) logger.debug(f"small circle: {j}") else: - logger.warning( - f"section{x_ratio.index(i)} small circle not detected" - ) + logger.warning(f"section{x_ratio.index(i)} small circle not detected") results = np.array(results) return results @@ -202,9 +187,7 @@ def detect_outliers(coords, threshold=0.1): std_avg_distance = np.std(avg_distances) # 找出距离均值超过阈值倍标准差的点 - outliers = np.where( - avg_distances > mean_avg_distance + threshold * std_avg_distance - )[0] + outliers = np.where(avg_distances > mean_avg_distance + threshold * std_avg_distance)[0] # 剔除异常点 filtered_coords = np.delete(coords, outliers, axis=0) @@ -214,191 +197,46 @@ def detect_outliers(coords, threshold=0.1): # 框架创建 def create_frame(cx, cy, r, high_tol=False): - k = 1.0401189 # 修正因子 - m = 4.7102526 # 中部padding - nums_bias = 0.9 - nums_bias_inn = 0.3 - nums_y = cy - 0.5 * r - - if high_tol == False: - t = 0.025 # 容差因子 - avatar = np.round( - np.array( - [ - [ - cx - r * t, - cy + r * t, - cx + 2 * k * r + r * t, - cy - 2 * k * r - r * t, - ], - [ - cx + 2 * k * r - r * t, - cy - 2 * k * r - r * t, - cx + 4 * k * r + r * t, - cy + r * t, - ], - [ - cx + 4 * k * r - r * t, - cy + r * t, - cx + 6 * k * r + r * t, - cy - 2 * k * r - r * t, - ], - [ - cx + (6 * k + m) * r - r * t, - cy + r * t, - cx + (8 * k + m) * r + r * t, - cy - 2 * k * r - r * t, - ], - [ - cx + (8 * k + m) * r - r * t, - cy - 2 * k * r - r * t, - cx + (10 * k + m) * r + r * t, - cy + r * t, - ], - [ - cx + (10 * k + m) * r - r * t, - cy + r * t, - cx + (12 * k + m) * r + r * t, - cy - 2 * k * r - r * t, - ], - ] - ) - ).astype("int") - nums = np.round( - np.array( - [ - [ - cx + (nums_bias + 0 * k) * r - r * t, - cy + r * t, - cx + (nums_bias_inn + 2 * k) * r + r * t, - nums_y, - ], - [ - cx + (nums_bias + 2 * k) * r - r * t, - nums_y, - cx + (nums_bias_inn + 4 * k) * r + r * t, - cy + r * t, - ], - [ - cx + (nums_bias + 4 * k) * r - r * t, - cy + r * t, - cx + (nums_bias_inn + 6 * k) * r + r * t, - nums_y, - ], - [ - cx + (-nums_bias_inn + 6 * k + m) * r - r * t, - cy + r * t, - cx + (-nums_bias + 8 * k + m) * r + r * t, - nums_y, - ], - [ - cx + (-nums_bias_inn + 8 * k + m) * r - r * t, - nums_y, - cx + (-nums_bias + 10 * k + m) * r + r * t, - cy + r * t, - ], - [ - cx + (-nums_bias_inn + 10 * k + m) * r - r * t, - cy + r * t, - cx + (-nums_bias + 12 * k + m) * r + r * t, - nums_y, - ], - ] - ) - ).astype("int") - - return avatar, nums - - if high_tol == True: - t = 0.17 # 容差因子 - avatar = np.round( - np.array( - [ - [ - cx - r * t, - cy + r * t, - cx + 2 * k * r + r * t, - cy - 2 * k * r - r * t, - ], - [ - cx + 2 * k * r - r * t, - cy - 2 * k * r - r * t, - cx + 4 * k * r + r * t, - cy + r * t, - ], - [ - cx + 4 * k * r - r * t, - cy + r * t, - cx + 6 * k * r + r * t, - cy - 2 * k * r - r * t, - ], - [ - cx + (6 * k + m) * r - r * t, - cy + r * t, - cx + (8 * k + m) * r + r * t, - cy - 2 * k * r - r * t, - ], - [ - cx + (8 * k + m) * r - r * t, - cy - 2 * k * r - r * t, - cx + (10 * k + m) * r + r * t, - cy + r * t, - ], - [ - cx + (10 * k + m) * r - r * t, - cy + r * t, - cx + (12 * k + m) * r + r * t, - cy - 2 * k * r - r * t, - ], - ] - ) - ).astype("int") - nums = np.round( - np.array( - [ - [ - cx + (nums_bias + 0 * k) * r - r * t, - cy + r * t, - cx + (nums_bias_inn + 2 * k) * r + r * t, - nums_y, - ], - [ - cx + (nums_bias + 2 * k) * r - r * t, - nums_y, - cx + (nums_bias_inn + 4 * k) * r + r * t, - cy + r * t, - ], - [ - cx + (nums_bias + 4 * k) * r - r * t, - cy + r * t, - cx + (nums_bias_inn + 6 * k) * r + r * t, - nums_y, - ], - [ - cx + (-nums_bias_inn + 6 * k + m) * r - r * t, - cy + r * t, - cx + (-nums_bias + 8 * k + m) * r + r * t, - nums_y, - ], - [ - cx + (-nums_bias_inn + 8 * k + m) * r - r * t, - nums_y, - cx + (-nums_bias + 10 * k + m) * r + r * t, - cy + r * t, - ], - [ - cx + (-nums_bias_inn + 10 * k + m) * r - r * t, - cy + r * t, - cx + (-nums_bias + 12 * k + m) * r + r * t, - nums_y, - ], - ] - ) - ).astype("int") - - return avatar, nums - - + k = 1.0401189 #修正因子 + m = 4.7102526 #中部padding + nums_bias=0.9 + nums_bias_inn=0.3 + nums_y=cy-0.5*r + + if high_tol==False: + t = 0.025 #容差因子 + avatar = np.round(np.array([[cx -r*t, cy +r*t, cx+2*k*r+r*t, cy-2*k*r-r*t], + [cx+2*k*r-r*t, cy-2*k*r-r*t, cx+4*k*r+r*t, cy +r*t], + [cx+4*k*r-r*t, cy +r*t, cx+6*k*r+r*t, cy-2*k*r-r*t], + [cx+(6 *k+m)*r-r*t, cy +r*t, cx+(8 *k+m)*r+r*t, cy-2*k*r-r*t], + [cx+(8 *k+m)*r-r*t, cy-2*k*r-r*t, cx+(10*k+m)*r+r*t, cy +r*t], + [cx+(10*k+m)*r-r*t, cy +r*t, cx+(12*k+m)*r+r*t, cy-2*k*r-r*t]])).astype("int") + nums = np.round(np.array([[cx+(nums_bias+0*k)*r-r*t, cy+r*t, cx+(nums_bias_inn+2*k)*r+r*t, nums_y], + [cx+(nums_bias+2*k)*r-r*t, nums_y, cx+(nums_bias_inn+4*k)*r+r*t, cy+r*t], + [cx+(nums_bias+4*k)*r-r*t, cy+r*t, cx+(nums_bias_inn+6*k)*r+r*t, nums_y], + [cx+(-nums_bias_inn+6 *k+m)*r-r*t, cy+r*t, cx+(-nums_bias+8 *k+m)*r+r*t, nums_y], + [cx+(-nums_bias_inn+8 *k+m)*r-r*t, nums_y, cx+(-nums_bias+10*k+m)*r+r*t, cy+r*t], + [cx+(-nums_bias_inn+10*k+m)*r-r*t, cy+r*t, cx+(-nums_bias+12*k+m)*r+r*t, nums_y]])).astype("int") + + return avatar,nums + + if high_tol==True: + t = 0.17 #容差因子 + avatar = np.round(np.array([[cx -r*t, cy +r*t, cx+2*k*r+r*t, cy-2*k*r-r*t], + [cx+2*k*r-r*t, cy-2*k*r-r*t, cx+4*k*r+r*t, cy +r*t], + [cx+4*k*r-r*t, cy +r*t, cx+6*k*r+r*t, cy-2*k*r-r*t], + [cx+(6 *k+m)*r-r*t, cy +r*t, cx+(8 *k+m)*r+r*t, cy-2*k*r-r*t], + [cx+(8 *k+m)*r-r*t, cy-2*k*r-r*t, cx+(10*k+m)*r+r*t, cy +r*t], + [cx+(10*k+m)*r-r*t, cy +r*t, cx+(12*k+m)*r+r*t, cy-2*k*r-r*t]])).astype("int") + nums = np.round(np.array([[cx+(nums_bias+0*k)*r-r*t, cy+r*t, cx+(nums_bias_inn+2*k)*r+r*t, nums_y], + [cx+(nums_bias+2*k)*r-r*t, nums_y, cx+(nums_bias_inn+4*k)*r+r*t, cy+r*t], + [cx+(nums_bias+4*k)*r-r*t, cy+r*t, cx+(nums_bias_inn+6*k)*r+r*t, nums_y], + [cx+(-nums_bias_inn+6 *k+m)*r-r*t, cy+r*t, cx+(-nums_bias+8 *k+m)*r+r*t, nums_y], + [cx+(-nums_bias_inn+8 *k+m)*r-r*t, nums_y, cx+(-nums_bias+10*k+m)*r+r*t, cy+r*t], + [cx+(-nums_bias_inn+10*k+m)*r-r*t, cy+r*t, cx+(-nums_bias+12*k+m)*r+r*t, nums_y]])).astype("int") + + return avatar,nums + def filter(results_big, results_small, height): high_tol = 0 big_key = 0 @@ -436,9 +274,7 @@ def filter(results_big, results_small, height): filtered_big = [] logger.warning("仅使用小圆进入最小二乘") else: - r_refer = ( - np.abs(filtered_small[1, 0] - filtered_small[0, 0]) / 16.39 - ) # 大圆的参考半径 + r_refer = np.abs(filtered_small[1, 0] - filtered_small[0, 0]) / 16.39 # 大圆的参考半径 diff_r = np.abs(results_big[:, 2] - r_refer) filtered_big = results_big[diff_r <= 0.05 * r_refer] # y筛选 @@ -447,9 +283,7 @@ def filter(results_big, results_small, height): # 如果数组为空,报错 if filtered_big.size == 0: logger.error(results_big) - raise IndexError( - "std_y筛选出现问题,请检查以上数据输入是否合法" - ) + raise IndexError("std_y筛选出现问题,请检查以上数据输入是否合法") mean_value = np.mean(filtered_big[:, 1]) abs_diff = np.abs(filtered_big[:, 1] - mean_value) @@ -464,9 +298,7 @@ def filter(results_big, results_small, height): if n in [0, 1, 2]: p_cx.append(x - ((2 * n + 1) * k * r_refer)) elif n in [3, 4, 5]: - p_cx.append( - x - ((2 * n + 1) * k * r_refer + 4.710 * r_refer) - ) + p_cx.append(x - ((2 * n + 1) * k * r_refer + 4.710 * r_refer)) filtered_big_p = np.column_stack((filtered_big, p_cx)) std_x = np.std(filtered_big_p[:, -1]) @@ -474,16 +306,12 @@ def filter(results_big, results_small, height): # 如果数组为空,报错 if filtered_big_p.size == 0: logger.error(results_big) - raise IndexError( - "std_x筛选出现问题,请检查以上数据输入是否合法" - ) + raise IndexError("std_x筛选出现问题,请检查以上数据输入是否合法") mean_value = np.mean(filtered_big_p[:, -1]) abs_diff = np.abs(filtered_big_p[:, -1] - mean_value) outlier_index = np.argmax(abs_diff) - filtered_big_p = np.delete( - filtered_big_p, outlier_index, axis=0 - ) + filtered_big_p = np.delete(filtered_big_p, outlier_index, axis=0) std_x = np.std(filtered_big_p[:, -1]) filtered_big = filtered_big_p[:, :-1] @@ -505,14 +333,10 @@ def filter(results_big, results_small, height): if n in [0, 1, 2]: a_cx = x - ((2 * n + 1) * k * r_refer) elif n in [3, 4, 5]: - a_cx = x - ( - (2 * n + 1) * k * r_refer + 4.710 * r_refer - ) + a_cx = x - ((2 * n + 1) * k * r_refer + 4.710 * r_refer) a_cy = y + radius std.append([a_cx, a_cy]) - _, out_index = detect_outliers( - std, threshold=0.02 * np.mean(results_big[:, 2]) - ) + _, out_index = detect_outliers(std, threshold=0.02 * np.mean(results_big[:, 2])) filtered_big = np.delete(results_big, out_index, axis=0) high_tol = 1 @@ -531,14 +355,10 @@ def filter(results_big, results_small, height): if n in [0, 1, 2]: a_cx = x - ((2 * n + 1) * k * r_refer) elif n in [3, 4, 5]: - a_cx = x - ( - (2 * n + 1) * k * r_refer + 4.710 * r_refer - ) + a_cx = x - ((2 * n + 1) * k * r_refer + 4.710 * r_refer) a_cy = y + radius std.append([a_cx, a_cy]) - _, out_index = detect_outliers( - std, threshold=0.02 * np.mean(results_big[:, 2]) - ) + _, out_index = detect_outliers(std, threshold=0.02 * np.mean(results_big[:, 2])) filtered_big = np.delete(results_big, out_index, axis=0) else: filtered_big = [] @@ -559,55 +379,25 @@ def cutFrame(image, high_tol=False): R_sets = flex_pixel(image) try: - crop_blur, crop_small, x_ratio, x_ratio_small = preprocess( - image, blur=11 - ) + crop_blur, crop_small, x_ratio, x_ratio_small = preprocess(image, blur=11) results_big = find_big( - crop_blur, - x_ratio, - minR=R_sets[0], - maxR=R_sets[1], - width=width, - p1=21, - p2=28, + crop_blur, x_ratio, minR=R_sets[0], maxR=R_sets[1], width=width, p1=21, p2=28 ) results_small = find_small( - crop_small, - x_ratio_small, - x_ratio, - minR=R_sets[2], - maxR=R_sets[3], - width=width, - ) - filtered_big, filtered_small, high_tol = filter( - results_big, results_small, height + crop_small, x_ratio_small, x_ratio, minR=R_sets[2], maxR=R_sets[3], width=width ) + filtered_big, filtered_small, high_tol = filter(results_big, results_small, height) except IndexError: try: - crop_blur, crop_small, x_ratio, x_ratio_small = preprocess( - image, blur=7, spare=1 - ) + crop_blur, crop_small, x_ratio, x_ratio_small = preprocess(image, blur=7, spare=1) results_big = find_big( - crop_blur, - x_ratio, - minR=R_sets[0], - maxR=R_sets[1], - width=width, - p1=18, - p2=24, + crop_blur, x_ratio, minR=R_sets[0], maxR=R_sets[1], width=width, p1=18, p2=24 ) results_small = find_small( - crop_small, - x_ratio_small, - x_ratio, - minR=R_sets[2], - maxR=R_sets[3], - width=width, - ) - filtered_big, filtered_small, high_tol = filter( - results_big, results_small, height + crop_small, x_ratio_small, x_ratio, minR=R_sets[2], maxR=R_sets[3], width=width ) + filtered_big, filtered_small, high_tol = filter(results_big, results_small, height) except IndexError: logger.error("备用参数捕捉失败!请重新框选试试") @@ -644,12 +434,8 @@ def residuals(params, large_circles, small_circles): # 定义目标函数 initial_guess = [0, 200, 60] # 使用最小二乘法求解 - result = custom_least_squares( - residuals, initial_guess, args=(filtered_big, filtered_small) - ) - avatar, nums = create_frame( - result.x[0], result.x[1], result.x[2], high_tol - ) + result = custom_least_squares(residuals, initial_guess, args=(filtered_big, filtered_small)) + avatar, nums = create_frame(result.x[0], result.x[1], result.x[2], high_tol) divisors = np.array([width, height, width, height]) d_avatar = avatar / divisors @@ -659,7 +445,7 @@ def residuals(params, large_circles, small_circles): # 定义目标函数 if __name__ == "__main__": - image = cv2.imread(str(ensure_tmp_images_dir() / "zone1.png")) + image = cv2.imread("images/tmp/zone1.png") height, width, _ = image.shape @@ -667,21 +453,10 @@ def residuals(params, large_circles, small_circles): # 定义目标函数 R_sets = flex_pixel(image) results_big = find_big( - crop_blur, - x_ratio, - minR=R_sets[0], - maxR=R_sets[1], - width=width, - p1=21, - p2=32, + crop_blur, x_ratio, minR=R_sets[0], maxR=R_sets[1], width=width, p1=21, p2=32 ) results_small = find_small( - crop_small, - x_ratio_small, - x_ratio, - minR=R_sets[2], - maxR=R_sets[3], - width=width, + crop_small, x_ratio_small, x_ratio, minR=R_sets[2], maxR=R_sets[3], width=width ) circles = np.round(results_big).astype("int") diff --git a/src/resources/assets/icons/background.png b/ico/background.png similarity index 100% rename from src/resources/assets/icons/background.png rename to ico/background.png diff --git a/src/resources/assets/icons/icon.ico b/ico/icon.ico similarity index 100% rename from src/resources/assets/icons/icon.ico rename to ico/icon.ico diff --git a/src/resources/assets/icons/icon_64x64.ico b/ico/icon_64x64.ico similarity index 100% rename from src/resources/assets/icons/icon_64x64.ico rename to ico/icon_64x64.ico diff --git "a/src/resources/assets/images/R-11\347\252\201\345\207\273\345\212\250\345\212\233\350\243\205\347\224\262.png" "b/images/R-11\347\252\201\345\207\273\345\212\250\345\212\233\350\243\205\347\224\262.png" similarity index 100% rename from "src/resources/assets/images/R-11\347\252\201\345\207\273\345\212\250\345\212\233\350\243\205\347\224\262.png" rename to "images/R-11\347\252\201\345\207\273\345\212\250\345\212\233\350\243\205\347\224\262.png" diff --git "a/src/resources/assets/images/R-31\351\207\215\345\236\213\345\212\250\345\212\233\350\243\205\347\224\262.png" "b/images/R-31\351\207\215\345\236\213\345\212\250\345\212\233\350\243\205\347\224\262.png" similarity index 100% rename from "src/resources/assets/images/R-31\351\207\215\345\236\213\345\212\250\345\212\233\350\243\205\347\224\262.png" rename to "images/R-31\351\207\215\345\236\213\345\212\250\345\212\233\350\243\205\347\224\262.png" diff --git a/src/resources/assets/images/eg.png b/images/eg.png similarity index 100% rename from src/resources/assets/images/eg.png rename to images/eg.png diff --git a/src/resources/assets/images/empty.png b/images/empty.png similarity index 100% rename from src/resources/assets/images/empty.png rename to images/empty.png diff --git a/src/resources/assets/images/login/announcement_close.png b/images/login/announcement_close.png similarity index 100% rename from src/resources/assets/images/login/announcement_close.png rename to images/login/announcement_close.png diff --git a/src/resources/assets/images/login/competition_page.png b/images/login/competition_page.png similarity index 100% rename from src/resources/assets/images/login/competition_page.png rename to images/login/competition_page.png diff --git a/src/resources/assets/images/login/event_claim_close.png b/images/login/event_claim_close.png similarity index 100% rename from src/resources/assets/images/login/event_claim_close.png rename to images/login/event_claim_close.png diff --git a/src/resources/assets/images/login/login_button.png b/images/login/login_button.png similarity index 100% rename from src/resources/assets/images/login/login_button.png rename to images/login/login_button.png diff --git a/src/resources/assets/images/login/login_off.png b/images/login/login_off.png similarity index 100% rename from src/resources/assets/images/login/login_off.png rename to images/login/login_off.png diff --git a/src/resources/assets/images/process/0.png b/images/process/0.png similarity index 100% rename from src/resources/assets/images/process/0.png rename to images/process/0.png diff --git a/src/resources/assets/images/process/1.png b/images/process/1.png similarity index 100% rename from src/resources/assets/images/process/1.png rename to images/process/1.png diff --git a/src/resources/assets/images/process/10.png b/images/process/10.png similarity index 100% rename from src/resources/assets/images/process/10.png rename to images/process/10.png diff --git a/src/resources/assets/images/process/11.png b/images/process/11.png similarity index 100% rename from src/resources/assets/images/process/11.png rename to images/process/11.png diff --git a/src/resources/assets/images/process/12.png b/images/process/12.png similarity index 100% rename from src/resources/assets/images/process/12.png rename to images/process/12.png diff --git a/src/resources/assets/images/process/13.png b/images/process/13.png similarity index 100% rename from src/resources/assets/images/process/13.png rename to images/process/13.png diff --git a/src/resources/assets/images/process/14.png b/images/process/14.png similarity index 100% rename from src/resources/assets/images/process/14.png rename to images/process/14.png diff --git a/src/resources/assets/images/process/15.png b/images/process/15.png similarity index 100% rename from src/resources/assets/images/process/15.png rename to images/process/15.png diff --git a/src/resources/assets/images/process/1746774840_28_5.png1s.png b/images/process/1746774840_28_5.png1s.png similarity index 100% rename from src/resources/assets/images/process/1746774840_28_5.png1s.png rename to images/process/1746774840_28_5.png1s.png diff --git a/src/resources/assets/images/process/2.png b/images/process/2.png similarity index 100% rename from src/resources/assets/images/process/2.png rename to images/process/2.png diff --git a/src/resources/assets/images/process/3.png b/images/process/3.png similarity index 100% rename from src/resources/assets/images/process/3.png rename to images/process/3.png diff --git a/src/resources/assets/images/process/4.png b/images/process/4.png similarity index 100% rename from src/resources/assets/images/process/4.png rename to images/process/4.png diff --git a/src/resources/assets/images/process/5.png b/images/process/5.png similarity index 100% rename from src/resources/assets/images/process/5.png rename to images/process/5.png diff --git a/src/resources/assets/images/process/6.png b/images/process/6.png similarity index 100% rename from src/resources/assets/images/process/6.png rename to images/process/6.png diff --git a/src/resources/assets/images/process/7.png b/images/process/7.png similarity index 100% rename from src/resources/assets/images/process/7.png rename to images/process/7.png diff --git a/src/resources/assets/images/process/8.png b/images/process/8.png similarity index 100% rename from src/resources/assets/images/process/8.png rename to images/process/8.png diff --git a/src/resources/assets/images/process/9.png b/images/process/9.png similarity index 100% rename from src/resources/assets/images/process/9.png rename to images/process/9.png diff --git "a/src/resources/assets/images/\342\200\234\345\244\215\344\273\207\350\200\205\342\200\235.png" "b/images/\342\200\234\345\244\215\344\273\207\350\200\205\342\200\235.png" similarity index 100% rename from "src/resources/assets/images/\342\200\234\345\244\215\344\273\207\350\200\205\342\200\235.png" rename to "images/\342\200\234\345\244\215\344\273\207\350\200\205\342\200\235.png" diff --git "a/src/resources/assets/images/\342\200\234\345\246\222\342\200\235.png" "b/images/\342\200\234\345\246\222\342\200\235.png" similarity index 100% rename from "src/resources/assets/images/\342\200\234\345\246\222\342\200\235.png" rename to "images/\342\200\234\345\246\222\342\200\235.png" diff --git "a/src/resources/assets/images/\342\200\234\345\267\242\347\251\264\342\200\235.png" "b/images/\342\200\234\345\267\242\347\251\264\342\200\235.png" similarity index 100% rename from "src/resources/assets/images/\342\200\234\345\267\242\347\251\264\342\200\235.png" rename to "images/\342\200\234\345\267\242\347\251\264\342\200\235.png" diff --git "a/src/resources/assets/images/\342\200\234\345\272\236\350\264\235\342\200\235.png" "b/images/\342\200\234\345\272\236\350\264\235\342\200\235.png" similarity index 100% rename from "src/resources/assets/images/\342\200\234\345\272\236\350\264\235\342\200\235.png" rename to "images/\342\200\234\345\272\236\350\264\235\342\200\235.png" diff --git "a/src/resources/assets/images/\342\200\234\346\212\225\347\237\263\346\234\272\342\200\235.png" "b/images/\342\200\234\346\212\225\347\237\263\346\234\272\342\200\235.png" similarity index 100% rename from "src/resources/assets/images/\342\200\234\346\212\225\347\237\263\346\234\272\342\200\235.png" rename to "images/\342\200\234\346\212\225\347\237\263\346\234\272\342\200\235.png" diff --git "a/src/resources/assets/images/\342\200\234\347\233\233\346\200\222\342\200\235.png" "b/images/\342\200\234\347\233\233\346\200\222\342\200\235.png" similarity index 100% rename from "src/resources/assets/images/\342\200\234\347\233\233\346\200\222\342\200\235.png" rename to "images/\342\200\234\347\233\233\346\200\222\342\200\235.png" diff --git "a/src/resources/assets/images/\342\200\234\350\207\252\345\234\250\342\200\235.png" "b/images/\342\200\234\350\207\252\345\234\250\342\200\235.png" similarity index 100% rename from "src/resources/assets/images/\342\200\234\350\207\252\345\234\250\342\200\235.png" rename to "images/\342\200\234\350\207\252\345\234\250\342\200\235.png" diff --git "a/src/resources/assets/images/\342\200\234\350\220\250\347\261\263\347\232\204\346\204\217\345\277\227\342\200\235.png" "b/images/\342\200\234\350\220\250\347\261\263\347\232\204\346\204\217\345\277\227\342\200\235.png" similarity index 100% rename from "src/resources/assets/images/\342\200\234\350\220\250\347\261\263\347\232\204\346\204\217\345\277\227\342\200\235.png" rename to "images/\342\200\234\350\220\250\347\261\263\347\232\204\346\204\217\345\277\227\342\200\235.png" diff --git "a/src/resources/assets/images/\342\200\234\351\222\263\351\222\263\347\224\237\351\243\216\342\200\235.png" "b/images/\342\200\234\351\222\263\351\222\263\347\224\237\351\243\216\342\200\235.png" similarity index 100% rename from "src/resources/assets/images/\342\200\234\351\222\263\351\222\263\347\224\237\351\243\216\342\200\235.png" rename to "images/\342\200\234\351\222\263\351\222\263\347\224\237\351\243\216\342\200\235.png" diff --git "a/src/resources/assets/images/\342\200\234\351\227\250\342\200\235.png" "b/images/\342\200\234\351\227\250\342\200\235.png" similarity index 100% rename from "src/resources/assets/images/\342\200\234\351\227\250\342\200\235.png" rename to "images/\342\200\234\351\227\250\342\200\235.png" diff --git "a/src/resources/assets/images/\342\200\234\351\230\277\345\222\254\342\200\235.png" "b/images/\342\200\234\351\230\277\345\222\254\342\200\235.png" similarity index 100% rename from "src/resources/assets/images/\342\200\234\351\230\277\345\222\254\342\200\235.png" rename to "images/\342\200\234\351\230\277\345\222\254\342\200\235.png" diff --git "a/src/resources/assets/images/\344\271\214\350\220\250\346\226\257\347\252\201\350\242\255\345\274\251\346\211\213.png" "b/images/\344\271\214\350\220\250\346\226\257\347\252\201\350\242\255\345\274\251\346\211\213.png" similarity index 100% rename from "src/resources/assets/images/\344\271\214\350\220\250\346\226\257\347\252\201\350\242\255\345\274\251\346\211\213.png" rename to "images/\344\271\214\350\220\250\346\226\257\347\252\201\350\242\255\345\274\251\346\211\213.png" diff --git "a/src/resources/assets/images/\345\205\250\345\260\201\351\227\255\346\262\231\346\273\251\350\275\246.png" "b/images/\345\205\250\345\260\201\351\227\255\346\262\231\346\273\251\350\275\246.png" similarity index 100% rename from "src/resources/assets/images/\345\205\250\345\260\201\351\227\255\346\262\231\346\273\251\350\275\246.png" rename to "images/\345\205\250\345\260\201\351\227\255\346\262\231\346\273\251\350\275\246.png" diff --git "a/src/resources/assets/images/\345\206\260\345\216\237\345\215\253\346\263\225\350\200\205.png" "b/images/\345\206\260\345\216\237\345\215\253\346\263\225\350\200\205.png" similarity index 100% rename from "src/resources/assets/images/\345\206\260\345\216\237\345\215\253\346\263\225\350\200\205.png" rename to "images/\345\206\260\345\216\237\345\215\253\346\263\225\350\200\205.png" diff --git "a/src/resources/assets/images/\345\206\260\345\216\237\346\234\257\345\270\210.png" "b/images/\345\206\260\345\216\237\346\234\257\345\270\210.png" similarity index 100% rename from "src/resources/assets/images/\345\206\260\345\216\237\346\234\257\345\270\210.png" rename to "images/\345\206\260\345\216\237\346\234\257\345\270\210.png" diff --git "a/src/resources/assets/images/\345\206\260\347\210\206\346\272\220\347\237\263\350\231\253.png" "b/images/\345\206\260\347\210\206\346\272\220\347\237\263\350\231\253.png" similarity index 100% rename from "src/resources/assets/images/\345\206\260\347\210\206\346\272\220\347\237\263\350\231\253.png" rename to "images/\345\206\260\347\210\206\346\272\220\347\237\263\350\231\253.png" diff --git "a/src/resources/assets/images/\345\207\213\351\233\266\351\252\221\345\243\253.png" "b/images/\345\207\213\351\233\266\351\252\221\345\243\253.png" similarity index 100% rename from "src/resources/assets/images/\345\207\213\351\233\266\351\252\221\345\243\253.png" rename to "images/\345\207\213\351\233\266\351\252\221\345\243\253.png" diff --git "a/src/resources/assets/images/\345\207\266\350\261\225\345\205\275.png" "b/images/\345\207\266\350\261\225\345\205\275.png" similarity index 100% rename from "src/resources/assets/images/\345\207\266\350\261\225\345\205\275.png" rename to "images/\345\207\266\350\261\225\345\205\275.png" diff --git "a/src/resources/assets/images/\345\217\215\350\243\205\347\224\262\346\255\245\345\205\265.png" "b/images/\345\217\215\350\243\205\347\224\262\346\255\245\345\205\265.png" similarity index 100% rename from "src/resources/assets/images/\345\217\215\350\243\205\347\224\262\346\255\245\345\205\265.png" rename to "images/\345\217\215\350\243\205\347\224\262\346\255\245\345\205\265.png" diff --git "a/src/resources/assets/images/\345\217\230\345\274\202\345\267\250\345\262\251\350\233\233.png" "b/images/\345\217\230\345\274\202\345\267\250\345\262\251\350\233\233.png" similarity index 100% rename from "src/resources/assets/images/\345\217\230\345\274\202\345\267\250\345\262\251\350\233\233.png" rename to "images/\345\217\230\345\274\202\345\267\250\345\262\251\350\233\233.png" diff --git "a/src/resources/assets/images/\345\221\274\345\225\270\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222.png" "b/images/\345\221\274\345\225\270\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222.png" similarity index 100% rename from "src/resources/assets/images/\345\221\274\345\225\270\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222.png" rename to "images/\345\221\274\345\225\270\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222.png" diff --git "a/src/resources/assets/images/\345\233\272\346\265\267\345\207\277\347\237\263\350\200\205.png" "b/images/\345\233\272\346\265\267\345\207\277\347\237\263\350\200\205.png" similarity index 100% rename from "src/resources/assets/images/\345\233\272\346\265\267\345\207\277\347\237\263\350\200\205.png" rename to "images/\345\233\272\346\265\267\345\207\277\347\237\263\350\200\205.png" diff --git "a/src/resources/assets/images/\345\244\215\344\273\207\350\200\205.png" "b/images/\345\244\215\344\273\207\350\200\205.png" similarity index 100% rename from "src/resources/assets/images/\345\244\215\344\273\207\350\200\205.png" rename to "images/\345\244\215\344\273\207\350\200\205.png" diff --git "a/src/resources/assets/images/\345\245\216\351\232\206\357\274\214\346\221\251\350\257\203\350\220\250\345\237\265\346\235\203\345\214\226.png" "b/images/\345\245\216\351\232\206\357\274\214\346\221\251\350\257\203\350\220\250\345\237\265\346\235\203\345\214\226.png" similarity index 100% rename from "src/resources/assets/images/\345\245\216\351\232\206\357\274\214\346\221\251\350\257\203\350\220\250\345\237\265\346\235\203\345\214\226.png" rename to "images/\345\245\216\351\232\206\357\274\214\346\221\251\350\257\203\350\220\250\345\237\265\346\235\203\345\214\226.png" diff --git "a/src/resources/assets/images/\345\256\277\344\270\273\346\265\201\346\265\252\350\200\205.png" "b/images/\345\256\277\344\270\273\346\265\201\346\265\252\350\200\205.png" similarity index 100% rename from "src/resources/assets/images/\345\256\277\344\270\273\346\265\201\346\265\252\350\200\205.png" rename to "images/\345\256\277\344\270\273\346\265\201\346\265\252\350\200\205.png" diff --git "a/src/resources/assets/images/\345\257\273\350\267\257\350\200\205\344\277\241\344\275\277.png" "b/images/\345\257\273\350\267\257\350\200\205\344\277\241\344\275\277.png" similarity index 100% rename from "src/resources/assets/images/\345\257\273\350\267\257\350\200\205\344\277\241\344\275\277.png" rename to "images/\345\257\273\350\267\257\350\200\205\344\277\241\344\275\277.png" diff --git "a/src/resources/assets/images/\345\261\261\346\265\267\344\274\227\347\252\245\351\255\205\344\272\272.png" "b/images/\345\261\261\346\265\267\344\274\227\347\252\245\351\255\205\344\272\272.png" similarity index 100% rename from "src/resources/assets/images/\345\261\261\346\265\267\344\274\227\347\252\245\351\255\205\344\272\272.png" rename to "images/\345\261\261\346\265\267\344\274\227\347\252\245\351\255\205\344\272\272.png" diff --git "a/src/resources/assets/images/\345\262\201\347\233\270.png" "b/images/\345\262\201\347\233\270.png" similarity index 100% rename from "src/resources/assets/images/\345\262\201\347\233\270.png" rename to "images/\345\262\201\347\233\270.png" diff --git "a/src/resources/assets/images/\345\262\251\345\206\240\345\205\275.png" "b/images/\345\262\251\345\206\240\345\205\275.png" similarity index 100% rename from "src/resources/assets/images/\345\262\251\345\206\240\345\205\275.png" rename to "images/\345\262\251\345\206\240\345\205\275.png" diff --git "a/src/resources/assets/images/\345\274\247\345\205\211\351\224\213\345\215\253\351\225\277.png" "b/images/\345\274\247\345\205\211\351\224\213\345\215\253\351\225\277.png" similarity index 100% rename from "src/resources/assets/images/\345\274\247\345\205\211\351\224\213\345\215\253\351\225\277.png" rename to "images/\345\274\247\345\205\211\351\224\213\345\215\253\351\225\277.png" diff --git "a/src/resources/assets/images/\346\213\263\346\211\213\345\233\232\347\212\257.png" "b/images/\346\213\263\346\211\213\345\233\232\347\212\257.png" similarity index 100% rename from "src/resources/assets/images/\346\213\263\346\211\213\345\233\232\347\212\257.png" rename to "images/\346\213\263\346\211\213\345\233\232\347\212\257.png" diff --git "a/src/resources/assets/images/\346\217\220\344\272\232\345\215\241\344\271\214\345\245\275\346\210\230\350\200\205.png" "b/images/\346\217\220\344\272\232\345\215\241\344\271\214\345\245\275\346\210\230\350\200\205.png" similarity index 100% rename from "src/resources/assets/images/\346\217\220\344\272\232\345\215\241\344\271\214\345\245\275\346\210\230\350\200\205.png" rename to "images/\346\217\220\344\272\232\345\215\241\344\271\214\345\245\275\346\210\230\350\200\205.png" diff --git "a/src/resources/assets/images/\346\217\220\344\272\232\345\215\241\344\271\214\347\240\264\345\235\217\347\216\213.png" "b/images/\346\217\220\344\272\232\345\215\241\344\271\214\347\240\264\345\235\217\347\216\213.png" similarity index 100% rename from "src/resources/assets/images/\346\217\220\344\272\232\345\215\241\344\271\214\347\240\264\345\235\217\347\216\213.png" rename to "images/\346\217\220\344\272\232\345\215\241\344\271\214\347\240\264\345\235\217\347\216\213.png" diff --git "a/src/resources/assets/images/\346\225\243\345\215\216\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222.png" "b/images/\346\225\243\345\215\216\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222.png" similarity index 100% rename from "src/resources/assets/images/\346\225\243\345\215\216\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222.png" rename to "images/\346\225\243\345\215\216\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222.png" diff --git "a/src/resources/assets/images/\346\227\272\350\264\242.png" "b/images/\346\227\272\350\264\242.png" similarity index 100% rename from "src/resources/assets/images/\346\227\272\350\264\242.png" rename to "images/\346\227\272\350\264\242.png" diff --git "a/src/resources/assets/images/\346\232\264\350\265\260\351\243\237\344\272\272\350\212\261.png" "b/images/\346\232\264\350\265\260\351\243\237\344\272\272\350\212\261.png" similarity index 100% rename from "src/resources/assets/images/\346\232\264\350\265\260\351\243\237\344\272\272\350\212\261.png" rename to "images/\346\232\264\350\265\260\351\243\237\344\272\272\350\212\261.png" diff --git "a/src/resources/assets/images/\346\232\264\350\265\260\351\243\237\350\231\253\350\212\261.png" "b/images/\346\232\264\350\265\260\351\243\237\350\231\253\350\212\261.png" similarity index 100% rename from "src/resources/assets/images/\346\232\264\350\265\260\351\243\237\350\231\253\350\212\261.png" rename to "images/\346\232\264\350\265\260\351\243\237\350\231\253\350\212\261.png" diff --git "a/src/resources/assets/images/\346\234\227\345\247\206\351\205\222\346\216\250\350\215\220\350\200\205.png" "b/images/\346\234\227\345\247\206\351\205\222\346\216\250\350\215\220\350\200\205.png" similarity index 100% rename from "src/resources/assets/images/\346\234\227\345\247\206\351\205\222\346\216\250\350\215\220\350\200\205.png" rename to "images/\346\234\227\345\247\206\351\205\222\346\216\250\350\215\220\350\200\205.png" diff --git "a/src/resources/assets/images/\346\235\260\346\226\257\351\241\277\302\267\345\250\201\345\273\211\345\247\206\346\226\257.png" "b/images/\346\235\260\346\226\257\351\241\277\302\267\345\250\201\345\273\211\345\247\206\346\226\257.png" similarity index 100% rename from "src/resources/assets/images/\346\235\260\346\226\257\351\241\277\302\267\345\250\201\345\273\211\345\247\206\346\226\257.png" rename to "images/\346\235\260\346\226\257\351\241\277\302\267\345\250\201\345\273\211\345\247\206\346\226\257.png" diff --git "a/src/resources/assets/images/\346\236\201\346\270\251\350\222\270\346\261\275\346\210\230\350\275\246.png" "b/images/\346\236\201\346\270\251\350\222\270\346\261\275\346\210\230\350\275\246.png" similarity index 100% rename from "src/resources/assets/images/\346\236\201\346\270\251\350\222\270\346\261\275\346\210\230\350\275\246.png" rename to "images/\346\236\201\346\270\251\350\222\270\346\261\275\346\210\230\350\275\246.png" diff --git "a/src/resources/assets/images/\346\236\243\345\244\247\345\210\200.png" "b/images/\346\236\243\345\244\247\345\210\200.png" similarity index 100% rename from "src/resources/assets/images/\346\236\243\345\244\247\345\210\200.png" rename to "images/\346\236\243\345\244\247\345\210\200.png" diff --git "a/src/resources/assets/images/\346\236\257\346\234\275\344\271\213\347\247\215.png" "b/images/\346\236\257\346\234\275\344\271\213\347\247\215.png" similarity index 100% rename from "src/resources/assets/images/\346\236\257\346\234\275\344\271\213\347\247\215.png" rename to "images/\346\236\257\346\234\275\344\271\213\347\247\215.png" diff --git "a/src/resources/assets/images/\346\243\230\345\205\275.png" "b/images/\346\243\230\345\205\275.png" similarity index 100% rename from "src/resources/assets/images/\346\243\230\345\205\275.png" rename to "images/\346\243\230\345\205\275.png" diff --git "a/src/resources/assets/images/\346\256\213\345\205\232\344\271\220\345\233\242\351\274\223\346\211\213.png" "b/images/\346\256\213\345\205\232\344\271\220\345\233\242\351\274\223\346\211\213.png" similarity index 100% rename from "src/resources/assets/images/\346\256\213\345\205\232\344\271\220\345\233\242\351\274\223\346\211\213.png" rename to "images/\346\256\213\345\205\232\344\271\220\345\233\242\351\274\223\346\211\213.png" diff --git "a/src/resources/assets/images/\346\256\213\345\205\232\350\220\250\345\205\213\346\226\257\346\211\213.png" "b/images/\346\256\213\345\205\232\350\220\250\345\205\213\346\226\257\346\211\213.png" similarity index 100% rename from "src/resources/assets/images/\346\256\213\345\205\232\350\220\250\345\205\213\346\226\257\346\211\213.png" rename to "images/\346\256\213\345\205\232\350\220\250\345\205\213\346\226\257\346\211\213.png" diff --git "a/src/resources/assets/images/\346\260\264\346\211\213\351\207\215\350\211\207.png" "b/images/\346\260\264\346\211\213\351\207\215\350\211\207.png" similarity index 100% rename from "src/resources/assets/images/\346\260\264\346\211\213\351\207\215\350\211\207.png" rename to "images/\346\260\264\346\211\213\351\207\215\350\211\207.png" diff --git "a/src/resources/assets/images/\346\260\264\351\201\201\345\277\215\350\200\205.png" "b/images/\346\260\264\351\201\201\345\277\215\350\200\205.png" similarity index 100% rename from "src/resources/assets/images/\346\260\264\351\201\201\345\277\215\350\200\205.png" rename to "images/\346\260\264\351\201\201\345\277\215\350\200\205.png" diff --git "a/src/resources/assets/images/\346\262\211\346\262\231.png" "b/images/\346\262\211\346\262\231.png" similarity index 100% rename from "src/resources/assets/images/\346\262\211\346\262\231.png" rename to "images/\346\262\211\346\262\231.png" diff --git "a/src/resources/assets/images/\346\262\270\350\241\200\351\252\221\345\243\253\345\233\242\347\262\276\351\224\220.png" "b/images/\346\262\270\350\241\200\351\252\221\345\243\253\345\233\242\347\262\276\351\224\220.png" similarity index 100% rename from "src/resources/assets/images/\346\262\270\350\241\200\351\252\221\345\243\253\345\233\242\347\262\276\351\224\220.png" rename to "images/\346\262\270\350\241\200\351\252\221\345\243\253\345\233\242\347\262\276\351\224\220.png" diff --git "a/src/resources/assets/images/\346\263\245\345\262\251\345\260\217\351\230\237\350\267\265\350\241\214\350\200\205.png" "b/images/\346\263\245\345\262\251\345\260\217\351\230\237\350\267\265\350\241\214\350\200\205.png" similarity index 100% rename from "src/resources/assets/images/\346\263\245\345\262\251\345\260\217\351\230\237\350\267\265\350\241\214\350\200\205.png" rename to "images/\346\263\245\345\262\251\345\260\217\351\230\237\350\267\265\350\241\214\350\200\205.png" diff --git "a/src/resources/assets/images/\346\263\245\345\262\251\345\267\250\345\203\217.png" "b/images/\346\263\245\345\262\251\345\267\250\345\203\217.png" similarity index 100% rename from "src/resources/assets/images/\346\263\245\345\262\251\345\267\250\345\203\217.png" rename to "images/\346\263\245\345\262\251\345\267\250\345\203\217.png" diff --git "a/src/resources/assets/images/\346\267\261\346\272\237\350\243\202\347\244\201\350\200\205.png" "b/images/\346\267\261\346\272\237\350\243\202\347\244\201\350\200\205.png" similarity index 100% rename from "src/resources/assets/images/\346\267\261\346\272\237\350\243\202\347\244\201\350\200\205.png" rename to "images/\346\267\261\346\272\237\350\243\202\347\244\201\350\200\205.png" diff --git "a/src/resources/assets/images/\346\270\251\351\241\272\347\232\204\346\255\246\350\243\205\351\251\256\345\205\275.png" "b/images/\346\270\251\351\241\272\347\232\204\346\255\246\350\243\205\351\251\256\345\205\275.png" similarity index 100% rename from "src/resources/assets/images/\346\270\251\351\241\272\347\232\204\346\255\246\350\243\205\351\251\256\345\205\275.png" rename to "images/\346\270\251\351\241\272\347\232\204\346\255\246\350\243\205\351\251\256\345\205\275.png" diff --git "a/src/resources/assets/images/\346\270\270\345\207\273\351\230\237\347\233\276\345\215\253.png" "b/images/\346\270\270\345\207\273\351\230\237\347\233\276\345\215\253.png" similarity index 100% rename from "src/resources/assets/images/\346\270\270\345\207\273\351\230\237\347\233\276\345\215\253.png" rename to "images/\346\270\270\345\207\273\351\230\237\347\233\276\345\215\253.png" diff --git "a/src/resources/assets/images/\346\271\226\347\225\224\345\277\227\346\204\277\350\200\205.png" "b/images/\346\271\226\347\225\224\345\277\227\346\204\277\350\200\205.png" similarity index 100% rename from "src/resources/assets/images/\346\271\226\347\225\224\345\277\227\346\204\277\350\200\205.png" rename to "images/\346\271\226\347\225\224\345\277\227\346\204\277\350\200\205.png" diff --git "a/src/resources/assets/images/\346\272\220\347\237\263\347\225\270\345\217\230\344\275\223.png" "b/images/\346\272\220\347\237\263\347\225\270\345\217\230\344\275\223.png" similarity index 100% rename from "src/resources/assets/images/\346\272\220\347\237\263\347\225\270\345\217\230\344\275\223.png" rename to "images/\346\272\220\347\237\263\347\225\270\345\217\230\344\275\223.png" diff --git "a/src/resources/assets/images/\347\201\274\347\203\255\346\272\220\347\237\263\350\231\253.png" "b/images/\347\201\274\347\203\255\346\272\220\347\237\263\350\231\253.png" similarity index 100% rename from "src/resources/assets/images/\347\201\274\347\203\255\346\272\220\347\237\263\350\231\253.png" rename to "images/\347\201\274\347\203\255\346\272\220\347\237\263\350\231\253.png" diff --git "a/src/resources/assets/images/\347\202\255\351\225\277\347\237\233.png" "b/images/\347\202\255\351\225\277\347\237\233.png" similarity index 100% rename from "src/resources/assets/images/\347\202\255\351\225\277\347\237\233.png" rename to "images/\347\202\255\351\225\277\347\237\233.png" diff --git "a/src/resources/assets/images/\347\202\256\345\207\273\347\273\204\351\225\277.png" "b/images/\347\202\256\345\207\273\347\273\204\351\225\277.png" similarity index 100% rename from "src/resources/assets/images/\347\202\256\345\207\273\347\273\204\351\225\277.png" rename to "images/\347\202\256\345\207\273\347\273\204\351\225\277.png" diff --git "a/src/resources/assets/images/\347\202\275\347\204\260\346\272\220\347\237\263\350\231\253.png" "b/images/\347\202\275\347\204\260\346\272\220\347\237\263\350\231\253.png" similarity index 100% rename from "src/resources/assets/images/\347\202\275\347\204\260\346\272\220\347\237\263\350\231\253.png" rename to "images/\347\202\275\347\204\260\346\272\220\347\237\263\350\231\253.png" diff --git "a/src/resources/assets/images/\347\213\202\346\232\264\345\256\277\344\270\273\347\273\204\351\225\277.png" "b/images/\347\213\202\346\232\264\345\256\277\344\270\273\347\273\204\351\225\277.png" similarity index 100% rename from "src/resources/assets/images/\347\213\202\346\232\264\345\256\277\344\270\273\347\273\204\351\225\277.png" rename to "images/\347\213\202\346\232\264\345\256\277\344\270\273\347\273\204\351\225\277.png" diff --git "a/src/resources/assets/images/\347\213\202\346\232\264\347\232\204\347\214\216\347\213\227pro.png" "b/images/\347\213\202\346\232\264\347\232\204\347\214\216\347\213\227pro.png" similarity index 100% rename from "src/resources/assets/images/\347\213\202\346\232\264\347\232\204\347\214\216\347\213\227pro.png" rename to "images/\347\213\202\346\232\264\347\232\204\347\214\216\347\213\227pro.png" diff --git "a/src/resources/assets/images/\347\213\231\345\207\273\346\255\245\345\205\265.png" "b/images/\347\213\231\345\207\273\346\255\245\345\205\265.png" similarity index 100% rename from "src/resources/assets/images/\347\213\231\345\207\273\346\255\245\345\205\265.png" rename to "images/\347\213\231\345\207\273\346\255\245\345\205\265.png" diff --git "a/src/resources/assets/images/\347\216\211\345\217\214\345\211\221.png" "b/images/\347\216\211\345\217\214\345\211\221.png" similarity index 100% rename from "src/resources/assets/images/\347\216\211\345\217\214\345\211\221.png" rename to "images/\347\216\211\345\217\214\345\211\221.png" diff --git "a/src/resources/assets/images/\347\224\260\351\274\267.png" "b/images/\347\224\260\351\274\267.png" similarity index 100% rename from "src/resources/assets/images/\347\224\260\351\274\267.png" rename to "images/\347\224\260\351\274\267.png" diff --git "a/src/resources/assets/images/\347\225\270\345\217\230\350\265\230\347\224\237\347\211\251.png" "b/images/\347\225\270\345\217\230\350\265\230\347\224\237\347\211\251.png" similarity index 100% rename from "src/resources/assets/images/\347\225\270\345\217\230\350\265\230\347\224\237\347\211\251.png" rename to "images/\347\225\270\345\217\230\350\265\230\347\224\237\347\211\251.png" diff --git "a/src/resources/assets/images/\347\230\264.png" "b/images/\347\230\264.png" similarity index 100% rename from "src/resources/assets/images/\347\230\264.png" rename to "images/\347\230\264.png" diff --git "a/src/resources/assets/images/\347\237\277\350\204\211\345\256\210\345\215\253.png" "b/images/\347\237\277\350\204\211\345\256\210\345\215\253.png" similarity index 100% rename from "src/resources/assets/images/\347\237\277\350\204\211\345\256\210\345\215\253.png" rename to "images/\347\237\277\350\204\211\345\256\210\345\215\253.png" diff --git "a/src/resources/assets/images/\347\240\201\345\244\264\346\260\264\346\211\213.png" "b/images/\347\240\201\345\244\264\346\260\264\346\211\213.png" similarity index 100% rename from "src/resources/assets/images/\347\240\201\345\244\264\346\260\264\346\211\213.png" rename to "images/\347\240\201\345\244\264\346\260\264\346\211\213.png" diff --git "a/src/resources/assets/images/\347\241\254\347\224\262\347\210\252\345\205\275.png" "b/images/\347\241\254\347\224\262\347\210\252\345\205\275.png" similarity index 100% rename from "src/resources/assets/images/\347\241\254\347\224\262\347\210\252\345\205\275.png" rename to "images/\347\241\254\347\224\262\347\210\252\345\205\275.png" diff --git "a/src/resources/assets/images/\347\251\272\351\231\215\345\205\265.png" "b/images/\347\251\272\351\231\215\345\205\265.png" similarity index 100% rename from "src/resources/assets/images/\347\251\272\351\231\215\345\205\265.png" rename to "images/\347\251\272\351\231\215\345\205\265.png" diff --git "a/src/resources/assets/images/\347\273\210\346\233\262\345\220\210\345\243\260.png" "b/images/\347\273\210\346\233\262\345\220\210\345\243\260.png" similarity index 100% rename from "src/resources/assets/images/\347\273\210\346\233\262\345\220\210\345\243\260.png" rename to "images/\347\273\210\346\233\262\345\220\210\345\243\260.png" diff --git "a/src/resources/assets/images/\350\205\220\350\264\245\351\252\221\345\243\253.png" "b/images/\350\205\220\350\264\245\351\252\221\345\243\253.png" similarity index 100% rename from "src/resources/assets/images/\350\205\220\350\264\245\351\252\221\345\243\253.png" rename to "images/\350\205\220\350\264\245\351\252\221\345\243\253.png" diff --git "a/src/resources/assets/images/\350\207\252\347\224\261\344\275\243\345\205\265\342\200\234\344\270\273\351\237\263\345\220\211\344\273\226\342\200\235.png" "b/images/\350\207\252\347\224\261\344\275\243\345\205\265\342\200\234\344\270\273\351\237\263\345\220\211\344\273\226\342\200\235.png" similarity index 100% rename from "src/resources/assets/images/\350\207\252\347\224\261\344\275\243\345\205\265\342\200\234\344\270\273\351\237\263\345\220\211\344\273\226\342\200\235.png" rename to "images/\350\207\252\347\224\261\344\275\243\345\205\265\342\200\234\344\270\273\351\237\263\345\220\211\344\273\226\342\200\235.png" diff --git "a/src/resources/assets/images/\350\207\252\347\224\261\344\275\243\345\205\265\342\200\234\350\264\235\346\226\257\342\200\235.png" "b/images/\350\207\252\347\224\261\344\275\243\345\205\265\342\200\234\350\264\235\346\226\257\342\200\235.png" similarity index 100% rename from "src/resources/assets/images/\350\207\252\347\224\261\344\275\243\345\205\265\342\200\234\350\264\235\346\226\257\342\200\235.png" rename to "images/\350\207\252\347\224\261\344\275\243\345\205\265\342\200\234\350\264\235\346\226\257\342\200\235.png" diff --git "a/src/resources/assets/images/\350\215\222\345\216\237\345\212\253\346\216\240\350\200\205.png" "b/images/\350\215\222\345\216\237\345\212\253\346\216\240\350\200\205.png" similarity index 100% rename from "src/resources/assets/images/\350\215\222\345\216\237\345\212\253\346\216\240\350\200\205.png" rename to "images/\350\215\222\345\216\237\345\212\253\346\216\240\350\200\205.png" diff --git "a/src/resources/assets/images/\350\220\250\345\215\241\345\205\271\345\244\247\345\211\221\346\211\213.png" "b/images/\350\220\250\345\215\241\345\205\271\345\244\247\345\211\221\346\211\213.png" similarity index 100% rename from "src/resources/assets/images/\350\220\250\345\215\241\345\205\271\345\244\247\345\211\221\346\211\213.png" rename to "images/\350\220\250\345\215\241\345\205\271\345\244\247\345\211\221\346\211\213.png" diff --git "a/src/resources/assets/images/\350\220\250\345\215\241\345\205\271\345\256\277\344\270\273\345\215\253\345\267\242\347\231\276\345\244\253\351\225\277.png" "b/images/\350\220\250\345\215\241\345\205\271\345\256\277\344\270\273\345\215\253\345\267\242\347\231\276\345\244\253\351\225\277.png" similarity index 100% rename from "src/resources/assets/images/\350\220\250\345\215\241\345\205\271\345\256\277\344\270\273\345\215\253\345\267\242\347\231\276\345\244\253\351\225\277.png" rename to "images/\350\220\250\345\215\241\345\205\271\345\256\277\344\270\273\345\215\253\345\267\242\347\231\276\345\244\253\351\225\277.png" diff --git "a/src/resources/assets/images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\345\220\236\345\231\254\350\200\205.png" "b/images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\345\220\236\345\231\254\350\200\205.png" similarity index 100% rename from "src/resources/assets/images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\345\220\236\345\231\254\350\200\205.png" rename to "images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\345\220\236\345\231\254\350\200\205.png" diff --git "a/src/resources/assets/images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\345\256\277\345\215\253.png" "b/images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\345\256\277\345\215\253.png" similarity index 100% rename from "src/resources/assets/images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\345\256\277\345\215\253.png" rename to "images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\345\256\277\345\215\253.png" diff --git "a/src/resources/assets/images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\346\210\230\350\275\246.png" "b/images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\346\210\230\350\275\246.png" similarity index 100% rename from "src/resources/assets/images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\346\210\230\350\275\246.png" rename to "images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\346\210\230\350\275\246.png" diff --git "a/src/resources/assets/images/\350\257\241\346\240\270\351\233\206\345\205\273\350\200\205.png" "b/images/\350\257\241\346\240\270\351\233\206\345\205\273\350\200\205.png" similarity index 100% rename from "src/resources/assets/images/\350\257\241\346\240\270\351\233\206\345\205\273\350\200\205.png" rename to "images/\350\257\241\346\240\270\351\233\206\345\205\273\350\200\205.png" diff --git "a/src/resources/assets/images/\350\265\217\351\207\221\347\214\216\344\272\272\345\274\251\346\211\213.png" "b/images/\350\265\217\351\207\221\347\214\216\344\272\272\345\274\251\346\211\213.png" similarity index 100% rename from "src/resources/assets/images/\350\265\217\351\207\221\347\214\216\344\272\272\345\274\251\346\211\213.png" rename to "images/\350\265\217\351\207\221\347\214\216\344\272\272\345\274\251\346\211\213.png" diff --git "a/src/resources/assets/images/\350\265\217\351\207\221\347\214\216\344\272\272\346\211\260\344\271\261\350\200\205.png" "b/images/\350\265\217\351\207\221\347\214\216\344\272\272\346\211\260\344\271\261\350\200\205.png" similarity index 100% rename from "src/resources/assets/images/\350\265\217\351\207\221\347\214\216\344\272\272\346\211\260\344\271\261\350\200\205.png" rename to "images/\350\265\217\351\207\221\347\214\216\344\272\272\346\211\260\344\271\261\350\200\205.png" diff --git "a/src/resources/assets/images/\350\277\267\350\267\257\347\232\204\345\267\250\345\203\217.png" "b/images/\350\277\267\350\267\257\347\232\204\345\267\250\345\203\217.png" similarity index 100% rename from "src/resources/assets/images/\350\277\267\350\267\257\347\232\204\345\267\250\345\203\217.png" rename to "images/\350\277\267\350\267\257\347\232\204\345\267\250\345\203\217.png" diff --git "a/src/resources/assets/images/\351\205\270\346\266\262\346\272\220\347\237\263\350\231\253\302\267\316\261.png" "b/images/\351\205\270\346\266\262\346\272\220\347\237\263\350\231\253\302\267\316\261.png" similarity index 100% rename from "src/resources/assets/images/\351\205\270\346\266\262\346\272\220\347\237\263\350\231\253\302\267\316\261.png" rename to "images/\351\205\270\346\266\262\346\272\220\347\237\263\350\231\253\302\267\316\261.png" diff --git "a/src/resources/assets/images/\351\207\215\350\243\205\351\230\262\345\276\241\350\200\205.png" "b/images/\351\207\215\350\243\205\351\230\262\345\276\241\350\200\205.png" similarity index 100% rename from "src/resources/assets/images/\351\207\215\350\243\205\351\230\262\345\276\241\350\200\205.png" rename to "images/\351\207\215\350\243\205\351\230\262\345\276\241\350\200\205.png" diff --git "a/src/resources/assets/images/\351\230\265\345\234\260\345\207\273\344\272\272\346\211\213.png" "b/images/\351\230\265\345\234\260\345\207\273\344\272\272\346\211\213.png" similarity index 100% rename from "src/resources/assets/images/\351\230\265\345\234\260\345\207\273\344\272\272\346\211\213.png" rename to "images/\351\230\265\345\234\260\345\207\273\344\272\272\346\211\213.png" diff --git "a/src/resources/assets/images/\351\243\216\346\203\205\350\241\227\342\200\234\346\230\237\346\234\257\345\270\210\342\200\235.png" "b/images/\351\243\216\346\203\205\350\241\227\342\200\234\346\230\237\346\234\257\345\270\210\342\200\235.png" similarity index 100% rename from "src/resources/assets/images/\351\243\216\346\203\205\350\241\227\342\200\234\346\230\237\346\234\257\345\270\210\342\200\235.png" rename to "images/\351\243\216\346\203\205\350\241\227\342\200\234\346\230\237\346\234\257\345\270\210\342\200\235.png" diff --git "a/src/resources/assets/images/\351\253\230\345\241\224\346\234\257\345\270\210.png" "b/images/\351\253\230\345\241\224\346\234\257\345\270\210.png" similarity index 100% rename from "src/resources/assets/images/\351\253\230\345\241\224\346\234\257\345\270\210.png" rename to "images/\351\253\230\345\241\224\346\234\257\345\270\210.png" diff --git "a/src/resources/assets/images/\351\253\230\346\231\256\345\260\274\345\205\213.png" "b/images/\351\253\230\346\231\256\345\260\274\345\205\213.png" similarity index 100% rename from "src/resources/assets/images/\351\253\230\346\231\256\345\260\274\345\205\213.png" rename to "images/\351\253\230\346\231\256\345\260\274\345\205\213.png" diff --git "a/src/resources/assets/images/\351\253\230\347\272\247\346\255\246\350\243\205\344\272\272\345\221\230.png" "b/images/\351\253\230\347\272\247\346\255\246\350\243\205\344\272\272\345\221\230.png" similarity index 100% rename from "src/resources/assets/images/\351\253\230\347\272\247\346\255\246\350\243\205\344\272\272\345\221\230.png" rename to "images/\351\253\230\347\272\247\346\255\246\350\243\205\344\272\272\345\221\230.png" diff --git "a/src/resources/assets/images/\351\253\230\350\203\275\346\272\220\347\237\263\350\231\253.png" "b/images/\351\253\230\350\203\275\346\272\220\347\237\263\350\231\253.png" similarity index 100% rename from "src/resources/assets/images/\351\253\230\350\203\275\346\272\220\347\237\263\350\231\253.png" rename to "images/\351\253\230\350\203\275\346\272\220\347\237\263\350\231\253.png" diff --git "a/src/resources/assets/images/\351\273\221\346\260\264\346\272\220\347\237\263\350\231\253.png" "b/images/\351\273\221\346\260\264\346\272\220\347\237\263\350\231\253.png" similarity index 100% rename from "src/resources/assets/images/\351\273\221\346\260\264\346\272\220\347\237\263\350\231\253.png" rename to "images/\351\273\221\346\260\264\346\272\220\347\237\263\350\231\253.png" diff --git a/src/ui/input_panel_ui.py b/input_panel_ui.py similarity index 80% rename from src/ui/input_panel_ui.py rename to input_panel_ui.py index 4a41ede..602ae10 100644 --- a/src/ui/input_panel_ui.py +++ b/input_panel_ui.py @@ -1,28 +1,14 @@ import json import re from collections import defaultdict -from PyQt6.QtWidgets import ( - QWidget, - QVBoxLayout, - QHBoxLayout, - QLabel, - QPushButton, - QFrame, -) -from PyQt6.QtWidgets import ( - QLineEdit, - QScrollArea, - QGridLayout, - QSizePolicy, - QGraphicsDropShadowEffect, -) -from PyQt6.QtCore import Qt, pyqtSignal -from PyQt6.QtGui import QPixmap, QColor +from PyQt6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QFrame +from PyQt6.QtWidgets import QLineEdit, QScrollArea, QGridLayout, QSizePolicy, QGraphicsDropShadowEffect +from PyQt6.QtCore import Qt, QTimer, pyqtSignal, QObject, QThread +from PyQt6.QtGui import QPixmap, QImage, QFont, QIcon, QPainter, QColor import numpy as np import logging -from src.core.config import MONSTER_COUNT, MONSTER_DATA, FIELD_FEATURE_COUNT -from src.core.paths import image_path, resource_path +from config import MONSTER_COUNT, MONSTER_DATA, FIELD_FEATURE_COUNT logger = logging.getLogger(__name__) @@ -31,10 +17,8 @@ class InputPanelUI(QFrame): # Signals to communicate with the main application predict_requested = pyqtSignal() reset_requested = pyqtSignal() - input_changed = ( - pyqtSignal() - ) # Signal emitted when any monster input changes - terrain_changed = pyqtSignal(list) # New signal for terrain changes + input_changed = pyqtSignal() # Signal emitted when any monster input changes + terrain_changed = pyqtSignal(list) # New signal for terrain changes # Terrain display mapping terrain_display_mapping = { @@ -49,7 +33,7 @@ class InputPanelUI(QFrame): "crossbow_top_crossbow": "顶部弩炮", "fire_side_crossbow": "侧边弩炮", "fire_side_fire": "侧边火炮", - "fire_top_fire": "顶部火炮", + "fire_top_fire": "顶部火炮" } @staticmethod @@ -59,19 +43,17 @@ def get_terrain_feature_columns(): """ try: # 加载类别映射 - class_map_path = resource_path( - "tools", "battlefield_recognize", "class_to_idx.json" - ) - with open(class_map_path, "r", encoding="utf-8") as f: + class_map_path = "tools/battlefield_recognize/class_to_idx.json" + with open(class_map_path, 'r', encoding='utf-8') as f: class_to_idx = json.load(f) # 使用与data_cleaning_with_field_recognize_gpu.py相同的逻辑 grouped_elements = defaultdict(list) for class_name in class_to_idx.keys(): - if class_name.endswith("_none"): + if class_name.endswith('_none'): continue - condensed_name = re.sub(r"_left_", "_", class_name) - condensed_name = re.sub(r"_right_", "_", condensed_name) + condensed_name = re.sub(r'_left_', '_', class_name) + condensed_name = re.sub(r'_right_', '_', condensed_name) grouped_elements[condensed_name].append(class_name) # 返回排序后的特征列名 @@ -85,21 +67,14 @@ def __init__(self): super().__init__() self.left_monsters: dict[str, str] = {} self.right_monsters: dict[str, str] = {} - self.terrain_buttons = {} # Initialize terrain buttons + self.terrain_buttons = {} # Initialize terrain buttons self.terrain_feature_columns = self.get_terrain_feature_columns() if not self.terrain_feature_columns: # If fetching fails, use default terrain features self.terrain_feature_columns = [ - "altar_vertical", - "block_parallel", - "block_vertical_altar", - "block_vertical_block", - "coil_narrow", - "coil_wide", - "crossbow_top", - "fire_side_left", - "fire_side_right", - "fire_top", + "altar_vertical", "block_parallel", "block_vertical_altar", + "block_vertical_block", "coil_narrow", "coil_wide", + "crossbow_top", "fire_side_left", "fire_side_right", "fire_top" ] self.init_ui() @@ -107,16 +82,16 @@ def __init__(self): def init_ui(self): self.setObjectName("input_panel_id") - self.setStyleSheet(""" + self.setStyleSheet( + """ QWidget#input_panel_id { background-color: rgba(0, 0, 0, 40); border-radius: 15px; border: 5px solid #F5EA2D; } - """) - self.setSizePolicy( - QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding + """ ) + self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) self.main_layout = QVBoxLayout(self) self.main_layout.setContentsMargins(0, 0, 0, 0) @@ -127,7 +102,8 @@ def init_ui(self): # 创建滚动区域 scroll = QScrollArea() scroll.setWidgetResizable(True) - scroll.setStyleSheet(""" + scroll.setStyleSheet( + """ QScrollBar:horizontal { background: rgba(0, 0, 0, 0); width: 12px; /* 宽度 */ @@ -171,7 +147,8 @@ def init_ui(self): min-height: 20px; border-radius: 6px; } - """) + """ + ) scroll_content = QWidget() self.scroll_grid = QGridLayout(scroll_content) @@ -228,7 +205,7 @@ def init_ui(self): # 分两行显示按钮,每行6个,更好地利用空间 terrain_rows = [] - for row_idx in range(2): # Changed from 3 to 2 rows + for row_idx in range(2): # Changed from 3 to 2 rows row_terrain = QWidget() row_layout = QHBoxLayout(row_terrain) row_layout.setSpacing(3) @@ -236,14 +213,13 @@ def init_ui(self): terrain_rows.append((row_terrain, row_layout)) for i, terrain_key in enumerate(self.terrain_feature_columns): - display_name = self.terrain_display_mapping.get( - terrain_key, terrain_key - ) + display_name = self.terrain_display_mapping.get(terrain_key, terrain_key) btn = QPushButton(display_name) btn.setCheckable(True) btn.setFixedHeight(26) - btn.setStyleSheet(""" + btn.setStyleSheet( + """ QPushButton { background-color: #7B7B7B; color: #FAFAFA; @@ -266,16 +242,13 @@ def init_ui(self): color: #313131; padding: 0px; } - """) - btn.clicked.connect( - lambda checked, k=terrain_key: self.on_terrain_multi_selected( - k - ) + """ ) + btn.clicked.connect(lambda checked, k=terrain_key: self.on_terrain_multi_selected(k)) self.terrain_buttons[terrain_key] = btn # 分两行显示,每行6个按钮 - row_index = i // 6 # Changed from 4 to 6 buttons per row + row_index = i // 6 # Changed from 4 to 6 buttons per row if row_index < len(terrain_rows): terrain_rows[row_index][1].addWidget(btn) else: @@ -298,7 +271,8 @@ def init_ui(self): # 预测按钮 - 带样式 self.predict_button = QPushButton("开始预测") self.predict_button.clicked.connect(self.predict_requested.emit) - self.predict_button.setStyleSheet(""" + self.predict_button.setStyleSheet( + """ QPushButton { background-color: #313131; color: #F3F31F; @@ -313,14 +287,14 @@ def init_ui(self): QPushButton:pressed { background-color: #212121; } - """) - self.predict_button.setSizePolicy( - QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed + """ ) + self.predict_button.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) self.reset_button = QPushButton("重置") self.reset_button.clicked.connect(self.reset_entries) - self.reset_button.setStyleSheet(""" + self.reset_button.setStyleSheet( + """ QPushButton { background-color: #313131; color: #F3F31F; @@ -335,7 +309,8 @@ def init_ui(self): QPushButton:pressed { background-color: #212121; } - """) + """ + ) result_button_layout.addWidget(self.predict_button) result_button_layout.addWidget(self.reset_button) @@ -371,9 +346,7 @@ def load_images(self): img_label.setAlignment(Qt.AlignmentFlag.AlignCenter) try: - pixmap = QPixmap( - str(image_path(MONSTER_DATA['原始名称'][i])) - ) + pixmap = QPixmap(f"images/{MONSTER_DATA['原始名称'][i]}.png") if not pixmap.isNull(): pixmap = pixmap.scaled( 60, @@ -398,9 +371,7 @@ def load_images(self): left_entry.setFixedWidth(60) left_entry.setPlaceholderText("左") left_entry.setAlignment(Qt.AlignmentFlag.AlignCenter) - left_entry.textChanged.connect( - self.input_changed.emit - ) # Connect to signal + left_entry.textChanged.connect(self.input_changed.emit) # Connect to signal self.left_monsters[str(i)] = left_entry # 右输入框 (放在左输入框下方) @@ -408,26 +379,16 @@ def load_images(self): right_entry.setFixedWidth(60) right_entry.setPlaceholderText("右") right_entry.setAlignment(Qt.AlignmentFlag.AlignCenter) - right_entry.textChanged.connect( - self.input_changed.emit - ) # Connect to signal + right_entry.textChanged.connect(self.input_changed.emit) # Connect to signal self.right_monsters[str(i)] = right_entry # 添加到容器 - container_layout.addWidget( - img_label, 0, Qt.AlignmentFlag.AlignCenter - ) - container_layout.addWidget( - left_entry, 0, Qt.AlignmentFlag.AlignCenter - ) - container_layout.addWidget( - right_entry, 0, Qt.AlignmentFlag.AlignCenter - ) + container_layout.addWidget(img_label, 0, Qt.AlignmentFlag.AlignCenter) + container_layout.addWidget(left_entry, 0, Qt.AlignmentFlag.AlignCenter) + container_layout.addWidget(right_entry, 0, Qt.AlignmentFlag.AlignCenter) # 添加到网格布局 - self.scroll_grid.addWidget( - monster_container, row, col, Qt.AlignmentFlag.AlignCenter - ) + self.scroll_grid.addWidget(monster_container, row, col, Qt.AlignmentFlag.AlignCenter) # 更新行列位置 col += 1 @@ -462,18 +423,14 @@ def set_monster_counts(self, left_counts: dict, right_counts: dict): if monster_id in self.left_monsters: self.left_monsters[monster_id].setText(str(count)) if count > 0: - self.left_monsters[monster_id].setStyleSheet( - "background-color: yellow;" - ) + self.left_monsters[monster_id].setStyleSheet("background-color: yellow;") else: self.left_monsters[monster_id].setStyleSheet("") for monster_id, count in right_counts.items(): if monster_id in self.right_monsters: self.right_monsters[monster_id].setText(str(count)) if count > 0: - self.right_monsters[monster_id].setStyleSheet( - "background-color: yellow;" - ) + self.right_monsters[monster_id].setStyleSheet("background-color: yellow;") else: self.right_monsters[monster_id].setStyleSheet("") self.input_changed.emit() @@ -501,9 +458,7 @@ def get_selected_terrains(self): def build_terrain_features(self, left_counts, right_counts): """构建包含地形的完整特征向量(支持多选地形)""" # Use the actual number of terrain feature columns - num_field_features = ( - len(self.terrain_feature_columns) if FIELD_FEATURE_COUNT else 0 - ) + num_field_features = len(self.terrain_feature_columns) if FIELD_FEATURE_COUNT else 0 # Build terrain feature vector terrain_features = np.zeros(num_field_features) @@ -517,9 +472,7 @@ def build_terrain_features(self, left_counts, right_counts): if terrain_idx < num_field_features: terrain_features[terrain_idx] = 1 else: - logger.warning( - f"Terrain {terrain} not in feature columns: {self.terrain_feature_columns}" - ) + logger.warning(f"Terrain {terrain} not in feature columns: {self.terrain_feature_columns}") logger.debug(f"Selected terrains: {selected_terrains}") logger.debug(f"Terrain feature vector: {terrain_features}") @@ -530,13 +483,11 @@ def build_terrain_features(self, left_counts, right_counts): # 1R-61R (Right monster features) # 62R-73R (Field features R, copied) - full_features = np.concatenate( - [ - left_counts, # 1L-61L (Left monster features) - terrain_features, # 62L-73L (Field features L) - right_counts, # 1R-61R (Right monster features) - terrain_features, # 62R-73R (Field features R, copied) - ] - ) + full_features = np.concatenate([ + left_counts, # 1L-61L (Left monster features) + terrain_features, # 62L-73L (Field features L) + right_counts, # 1R-61R (Right monster features) + terrain_features # 62R-73R (Field features R, copied) + ]) return full_features diff --git a/src/data/load_data.py b/loadData.py similarity index 76% rename from src/data/load_data.py rename to loadData.py index 4c6f6d6..5481459 100644 --- a/src/data/load_data.py +++ b/loadData.py @@ -5,18 +5,21 @@ import logging import gzip import win32gui +import win32api import win32con +import sys import os from pathlib import Path -from src.game.winrt_capture import WinRTScreenCapture +from winrt_capture import WinRTScreenCapture logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) + class AdbConnector: def __init__(self, adb_serial=None): - self.adb_path = r".\vendor\bin\platform-tools\adb.exe" + self.adb_path = r".\platform-tools\adb.exe" self.screen_width = 0 self.screen_height = 0 self.device_serial = adb_serial if adb_serial else "" @@ -26,9 +29,7 @@ def connect(self): # 初始化设备序列号 try: # 如果已经有序列号,则尝试更新该序列号;否则使用默认值 - target_serial = ( - self.device_serial if self.device_serial else "127.0.0.1:5555" - ) + target_serial = self.device_serial if self.device_serial else "127.0.0.1:5555" self.update_device_serial(target_serial) logger.info(f"最终使用设备: {self.device_serial}") except RuntimeError as e: @@ -62,11 +63,7 @@ def get_window_size(self): # 执行ADB命令获取分辨率 size_cmd = f"{self.adb_path} -s {self.device_serial} shell wm size" result = subprocess.run( - size_cmd, - shell=True, - capture_output=True, - text=True, - check=True, + size_cmd, shell=True, capture_output=True, text=True, check=True ) output = result.stdout.strip() @@ -87,13 +84,9 @@ def get_window_size(self): else: screen_width = height screen_height = width - logger.info( - f"成功获取模拟器分辨率: {screen_width}x{screen_height}" - ) + logger.info(f"成功获取模拟器分辨率: {screen_width}x{screen_height}") except Exception as e: # 否则使用默认分辨率 - logger.exception( - f"获取分辨率失败,使用默认分辨率1920x1080。错误: {e}" - ) + logger.exception(f"获取分辨率失败,使用默认分辨率1920x1080。错误: {e}") screen_width = 1920 screen_height = 1080 return screen_width, screen_height @@ -102,11 +95,7 @@ def get_device_list(self): try: device_cmd = f"{self.adb_path} devices" result = subprocess.run( - device_cmd, - shell=True, - capture_output=True, - text=True, - timeout=5, + device_cmd, shell=True, capture_output=True, text=True, timeout=5 ) devices: list[str] = [] for line in result.stdout.split("\n"): @@ -129,11 +118,7 @@ def update_device_serial(self, serial): # 检查手动设备是否在线 device_cmd = f"{self.adb_path} devices" result = subprocess.run( - device_cmd, - shell=True, - capture_output=True, - text=True, - timeout=5, + device_cmd, shell=True, capture_output=True, text=True, timeout=5 ) # 只在调试模式下输出完整设备列表 logger.debug(f"ADB devices输出:\n{result.stdout}") @@ -149,9 +134,7 @@ def update_device_serial(self, serial): return dev # 只使用指定的设备,不要自动选择其他设备 - logger.error( - f"未找到指定的设备: {serial},当前在线设备: {devices}" - ) + logger.error(f"未找到指定的设备: {serial},当前在线设备: {devices}") self.device_serial = "" return "" @@ -205,14 +188,10 @@ def decode_raw(self, data: bytes): # 确保数据长度正确(实际屏幕分辨率,4通道) expected_length = self.screen_width * self.screen_height * 4 if len(argb_array) != expected_length: - raise ValueError( - f"Invalid data length for {self.screen_width}x{self.screen_height} ARGB image" - ) + raise ValueError(f"Invalid data length for {self.screen_width}x{self.screen_height} ARGB image") # 转换为正确的形状 (高度, 宽度, 通道) - argb_array = argb_array.reshape( - (self.screen_height, self.screen_width, 4) - ) + argb_array = argb_array.reshape((self.screen_height, self.screen_width, 4)) # 分离Alpha通道(如果需要保留Alpha,可以去掉这步) # 这里将ARGB转换为BGR(OpenCV默认格式) @@ -229,19 +208,17 @@ def decode_raw_with_gzip(self, data: bytes): image = self.decode_raw(decompressed_data) return image except Exception as e: - logger.exception( - "Gzip decompression or image decoding failed: %s", e - ) + logger.exception("Gzip decompression or image decoding failed: %s", e) return None def capture_screenshot_raw_gzip(self): - get_raw_gzip_cmd = rf'{self.adb_path} -s {self.device_serial} exec-out "screencap | gzip -1"' + get_raw_gzip_cmd = ( + rf'{self.adb_path} -s {self.device_serial} exec-out "screencap | gzip -1"' + ) ta = time.time() try: # 获取经过gzip压缩的二进制图像数据 - screenshot_raw_gzip = subprocess.check_output( - get_raw_gzip_cmd, shell=True - ) + screenshot_raw_gzip = subprocess.check_output(get_raw_gzip_cmd, shell=True) image = self.decode_raw_with_gzip(screenshot_raw_gzip) if image is None: raise RuntimeError("OpenCV failed to decode image") @@ -283,51 +260,39 @@ def connect(self): rect = win32gui.GetClientRect(self.hwnd) self.screen_width = rect[2] - rect[0] self.screen_height = rect[3] - rect[1] - + try: - from ..game.maa_adb_connector import resolve_maafw_path - + from maa_adb_connector import resolve_maafw_path binary_path = resolve_maafw_path() if binary_path: os.environ["MAAFW_BINARY_PATH"] = binary_path - + from maa.toolkit import Toolkit - from maa.controller import ( - Win32Controller, - MaaWin32ScreencapMethodEnum, - MaaWin32InputMethodEnum, - ) - + from maa.controller import Win32Controller, MaaWin32ScreencapMethodEnum, MaaWin32InputMethodEnum Toolkit.init_option(str(Path.cwd())) - + # 既然纯 SendMessage 被引擎无视,我们退一步使用 SendMessageWithCursorPos # 它会在瞬间把鼠标光标移动到目标位置发送消息,再瞬间移回原位。这种方式可能不会强制将游戏窗口调回前台。 self.maa_ctrl = Win32Controller( self.hwnd, screencap_method=MaaWin32ScreencapMethodEnum.FramePool, mouse_method=MaaWin32InputMethodEnum.SendMessageWithCursorPos, - keyboard_method=MaaWin32InputMethodEnum.SendMessageWithCursorPos, + keyboard_method=MaaWin32InputMethodEnum.SendMessageWithCursorPos ) self.maa_ctrl.post_connection().wait() - + # 设置截图使用原始大小,防止因为MAA默认缩放导致外部坐标及图像切割计算出错 self.maa_ctrl.set_screenshot_use_raw_size(True) - - logger.info( - f"已成功通过 MaaFramework 接管 PC 窗口 (支持后台操作)" - ) + + logger.info(f"已成功通过 MaaFramework 接管 PC 窗口 (支持后台操作)") except Exception as e: - logger.warning( - f"MaaFramework 初始化失败,退回原有前台实现: {e}" - ) + logger.warning(f"MaaFramework 初始化失败,退回原有前台实现: {e}") self.maa_ctrl = None self.capture = WinRTScreenCapture(window_name=self.window_name) self.capture.start() self.is_connected = True - logger.info( - f"成功连接到PC端窗口: {self.window_name}, 分辨率: {self.screen_width}x{self.screen_height}" - ) + logger.info(f"成功连接到PC端窗口: {self.window_name}, 分辨率: {self.screen_width}x{self.screen_height}") else: logger.warning(f"未找到PC端窗口: {self.window_name}") self.is_connected = False @@ -335,7 +300,7 @@ def connect(self): def capture_screenshot(self): if not self.is_connected: return None - + if self.maa_ctrl: try: self.maa_ctrl.post_screencap().wait() @@ -352,7 +317,7 @@ def capture_screenshot(self): def click(self, point): if not self.hwnd: return - + try: # PC端点击需要重新获取一次ClientRect以防窗口大小改变 rect = win32gui.GetClientRect(self.hwnd) @@ -362,21 +327,17 @@ def click(self, point): x, y = point x_coord = int(x * self.screen_width) y_coord = int(y * self.screen_height) - + if self.maa_ctrl: logger.info(f"MAA后台点击坐标: ({x_coord}, {y_coord})") self.maa_ctrl.post_click(x_coord, y_coord).wait() return - client_left, client_top = win32gui.ClientToScreen( - self.hwnd, (0, 0) - ) + client_left, client_top = win32gui.ClientToScreen(self.hwnd, (0, 0)) screen_x = client_left + x_coord screen_y = client_top + y_coord - - logger.info( - f"PC端点击坐标: 窗口内({x_coord}, {y_coord}) -> 屏幕({screen_x}, {screen_y})" - ) + + logger.info(f"PC端点击坐标: 窗口内({x_coord}, {y_coord}) -> 屏幕({screen_x}, {screen_y})") # 尝试将窗口置于前台,忽略可能的错误 try: @@ -390,42 +351,16 @@ def click(self, point): # 使用底层 SendInput 模拟鼠标事件,支持多显示器 (VIRTUALDESK) 并且不会在权限不足时抛出异常 import ctypes - + PUL = ctypes.POINTER(ctypes.c_ulong) - class KeyBdInput(ctypes.Structure): - _fields_ = [ - ("wVk", ctypes.c_ushort), - ("wScan", ctypes.c_ushort), - ("dwFlags", ctypes.c_ulong), - ("time", ctypes.c_ulong), - ("dwExtraInfo", PUL), - ] - + _fields_ = [("wVk", ctypes.c_ushort), ("wScan", ctypes.c_ushort), ("dwFlags", ctypes.c_ulong), ("time", ctypes.c_ulong), ("dwExtraInfo", PUL)] class HardwareInput(ctypes.Structure): - _fields_ = [ - ("uMsg", ctypes.c_ulong), - ("wParamL", ctypes.c_short), - ("wParamH", ctypes.c_ushort), - ] - + _fields_ = [("uMsg", ctypes.c_ulong), ("wParamL", ctypes.c_short), ("wParamH", ctypes.c_ushort)] class MouseInput(ctypes.Structure): - _fields_ = [ - ("dx", ctypes.c_long), - ("dy", ctypes.c_long), - ("mouseData", ctypes.c_ulong), - ("dwFlags", ctypes.c_ulong), - ("time", ctypes.c_ulong), - ("dwExtraInfo", PUL), - ] - + _fields_ = [("dx", ctypes.c_long), ("dy", ctypes.c_long), ("mouseData", ctypes.c_ulong), ("dwFlags", ctypes.c_ulong), ("time", ctypes.c_ulong), ("dwExtraInfo", PUL)] class Input_I(ctypes.Union): - _fields_ = [ - ("ki", KeyBdInput), - ("mi", MouseInput), - ("hi", HardwareInput), - ] - + _fields_ = [("ki", KeyBdInput), ("mi", MouseInput), ("hi", HardwareInput)] class Input(ctypes.Structure): _fields_ = [("type", ctypes.c_ulong), ("ii", Input_I)] @@ -433,19 +368,11 @@ class Input(ctypes.Structure): SM_YVIRTUALSCREEN = 77 SM_CXVIRTUALSCREEN = 78 SM_CYVIRTUALSCREEN = 79 - - vscreen_x = ctypes.windll.user32.GetSystemMetrics( - SM_XVIRTUALSCREEN - ) - vscreen_y = ctypes.windll.user32.GetSystemMetrics( - SM_YVIRTUALSCREEN - ) - vscreen_w = ctypes.windll.user32.GetSystemMetrics( - SM_CXVIRTUALSCREEN - ) - vscreen_h = ctypes.windll.user32.GetSystemMetrics( - SM_CYVIRTUALSCREEN - ) + + vscreen_x = ctypes.windll.user32.GetSystemMetrics(SM_XVIRTUALSCREEN) + vscreen_y = ctypes.windll.user32.GetSystemMetrics(SM_YVIRTUALSCREEN) + vscreen_w = ctypes.windll.user32.GetSystemMetrics(SM_CXVIRTUALSCREEN) + vscreen_h = ctypes.windll.user32.GetSystemMetrics(SM_CYVIRTUALSCREEN) if vscreen_w == 0 or vscreen_h == 0: vscreen_w = 1920 @@ -463,58 +390,25 @@ class Input(ctypes.Structure): extra = ctypes.c_ulong(0) ii_ = Input_I() - + # 移动鼠标 - ii_.mi = MouseInput( - dx, - dy, - 0, - MOUSEEVENTF_MOVE - | MOUSEEVENTF_ABSOLUTE - | MOUSEEVENTF_VIRTUALDESK, - 0, - ctypes.pointer(extra), - ) + ii_.mi = MouseInput(dx, dy, 0, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK, 0, ctypes.pointer(extra)) cmd = Input(ctypes.c_ulong(0), ii_) - ctypes.windll.user32.SendInput( - 1, ctypes.pointer(cmd), ctypes.sizeof(cmd) - ) - + ctypes.windll.user32.SendInput(1, ctypes.pointer(cmd), ctypes.sizeof(cmd)) + time.sleep(0.05) - + # 按下左键 - ii_.mi = MouseInput( - dx, - dy, - 0, - MOUSEEVENTF_LEFTDOWN - | MOUSEEVENTF_ABSOLUTE - | MOUSEEVENTF_VIRTUALDESK, - 0, - ctypes.pointer(extra), - ) + ii_.mi = MouseInput(dx, dy, 0, MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK, 0, ctypes.pointer(extra)) cmd = Input(ctypes.c_ulong(0), ii_) - ctypes.windll.user32.SendInput( - 1, ctypes.pointer(cmd), ctypes.sizeof(cmd) - ) - + ctypes.windll.user32.SendInput(1, ctypes.pointer(cmd), ctypes.sizeof(cmd)) + time.sleep(0.05) - + # 抬起左键 - ii_.mi = MouseInput( - dx, - dy, - 0, - MOUSEEVENTF_LEFTUP - | MOUSEEVENTF_ABSOLUTE - | MOUSEEVENTF_VIRTUALDESK, - 0, - ctypes.pointer(extra), - ) + ii_.mi = MouseInput(dx, dy, 0, MOUSEEVENTF_LEFTUP | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK, 0, ctypes.pointer(extra)) cmd = Input(ctypes.c_ulong(0), ii_) - ctypes.windll.user32.SendInput( - 1, ctypes.pointer(cmd), ctypes.sizeof(cmd) - ) + ctypes.windll.user32.SendInput(1, ctypes.pointer(cmd), ctypes.sizeof(cmd)) except Exception as e: logger.exception(f"PC端点击出错: {e}") diff --git a/src/game/login.py b/login.py similarity index 72% rename from src/game/login.py rename to login.py index 6128b8d..fea67ae 100644 --- a/src/game/login.py +++ b/login.py @@ -1,7 +1,6 @@ import os - # 设置 OpenCV 日志级别为 ERROR,减少 libpng 警告 -os.environ["OPENCV_LOG_LEVEL"] = "ERROR" +os.environ['OPENCV_LOG_LEVEL'] = 'ERROR' import logging import time @@ -10,18 +9,15 @@ import cv2 import numpy as np -from src.core.paths import PROCESS_IMAGES_DIR, process_image_path - logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) - class LoginManager: """登录管理器,处理游戏登录和页面跳转""" - + def __init__(self, connector, max_restart_count=3): self.connector = connector - self.template_dir = PROCESS_IMAGES_DIR / "login" + self.template_dir = Path("images") / "login" self.template_dir.mkdir(parents=True, exist_ok=True) self.restart_count = 0 self.max_restart_count = max_restart_count @@ -30,7 +26,7 @@ def __init__(self, connector, max_restart_count=3): except Exception as e: logger.error(f"模板加载失败: {e}") self.templates = {} - + def _log(self, level, message): """生成带有设备序列号的日志消息""" serial = getattr(self.connector, "device_serial", None) @@ -38,16 +34,16 @@ def _log(self, level, message): logger.log(level, f"[{serial}] {message}") else: logger.log(level, message) - + def reset_restart_count(self): """重置重启计数器""" self.restart_count = 0 self._log(logging.INFO, "重启计数器已重置") - + def can_restart(self): """检查是否可以重启""" return self.restart_count < self.max_restart_count - + def _load_templates(self): """加载模板图片""" self.templates = {} @@ -58,51 +54,38 @@ def _load_templates(self): if template is not None: self.templates[template_name] = template logger.info(f"加载模板: {template_name}") - + def match_template(self, screenshot, template_name, threshold=0.9): """匹配模板""" if template_name not in self.templates: logger.error(f"模板不存在: {template_name}") return False, (0, 0) - + template = self.templates[template_name] - + # 转换为灰度图像 screenshot_gray = cv2.cvtColor(screenshot, cv2.COLOR_BGR2GRAY) template_gray = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY) - + # 多尺度模板匹配 found = None for scale in np.linspace(0.5, 1.5, 10): # 调整模板大小 - resized = cv2.resize( - template_gray, - ( - int(template_gray.shape[1] * scale), - int(template_gray.shape[0] * scale), - ), - ) - if ( - resized.shape[0] > screenshot_gray.shape[0] - or resized.shape[1] > screenshot_gray.shape[1] - ): + resized = cv2.resize(template_gray, (int(template_gray.shape[1] * scale), int(template_gray.shape[0] * scale))) + if resized.shape[0] > screenshot_gray.shape[0] or resized.shape[1] > screenshot_gray.shape[1]: break - + # 匹配 - result = cv2.matchTemplate( - screenshot_gray, resized, cv2.TM_CCOEFF_NORMED - ) + result = cv2.matchTemplate(screenshot_gray, resized, cv2.TM_CCOEFF_NORMED) min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result) - + # 更新最佳匹配 if found is None or max_val > found[0]: found = (max_val, max_loc, scale) - + if found: max_val, max_loc, scale = found - h, w = int(template.shape[0] * scale), int( - template.shape[1] * scale - ) + h, w = int(template.shape[0] * scale), int(template.shape[1] * scale) if max_val >= threshold: logger.info(f"匹配到模板 {template_name},置信度: {max_val}") # 返回模板中心点坐标 @@ -110,29 +93,26 @@ def match_template(self, screenshot, template_name, threshold=0.9): center_y = max_loc[1] + h // 2 return True, (center_x, center_y) else: - logger.debug( - f"未匹配到模板 {template_name},最高置信度: {max_val}" - ) + logger.debug(f"未匹配到模板 {template_name},最高置信度: {max_val}") return False, (0, 0) else: logger.debug(f"未匹配到模板 {template_name}") return False, (0, 0) + + def restart_game(self): """重启游戏""" self._log(logging.INFO, "开始重启游戏") - + # 确定连接类型 - is_pc = hasattr(self.connector, "hwnd") and self.connector.hwnd - is_adb = ( - hasattr(self.connector, "device_serial") - and self.connector.device_serial - ) - + is_pc = hasattr(self.connector, 'hwnd') and self.connector.hwnd + is_adb = hasattr(self.connector, 'device_serial') and self.connector.device_serial + if not is_pc and not is_adb: self._log(logging.ERROR, "无法确定连接类型,无法重启游戏") return False - + # 关闭游戏进程 try: if is_pc: @@ -140,101 +120,71 @@ def restart_game(self): import win32gui import win32process import win32api - - _, process_id = win32process.GetWindowThreadProcessId( - self.connector.hwnd - ) + _, process_id = win32process.GetWindowThreadProcessId(self.connector.hwnd) process = win32api.OpenProcess(1, False, process_id) win32api.TerminateProcess(process, 0) win32api.CloseHandle(process) self._log(logging.INFO, "关闭游戏进程成功") else: # 对于ADB端,关闭游戏进程 - adb_path = getattr(self.connector, "adb_path", "adb") - subprocess.run( - f"{adb_path} -s {self.connector.device_serial} shell am force-stop com.hypergryph.arknights", - shell=True, - ) + adb_path = getattr(self.connector, 'adb_path', 'adb') + subprocess.run(f"{adb_path} -s {self.connector.device_serial} shell am force-stop com.hypergryph.arknights", shell=True) self._log(logging.INFO, "关闭游戏进程成功") except Exception as e: self._log(logging.ERROR, f"关闭游戏进程失败: {e}") return False - + # 等待一段时间后重新启动游戏 time.sleep(3) - + try: if is_pc: # 对于PC端,重新启动游戏 # 尝试从注册表获取游戏路径 try: import winreg - - key = winreg.OpenKey( - winreg.HKEY_LOCAL_MACHINE, - r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", - ) + key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall") for i in range(winreg.QueryInfoKey(key)[0]): subkey_name = winreg.EnumKey(key, i) subkey = winreg.OpenKey(key, subkey_name) try: - display_name = winreg.QueryValueEx( - subkey, "DisplayName" - )[0] + display_name = winreg.QueryValueEx(subkey, "DisplayName")[0] if "Arknights" in display_name: - install_location = winreg.QueryValueEx( - subkey, "InstallLocation" - )[0] - game_path = ( - Path(install_location) / "Arknights.exe" - ) + install_location = winreg.QueryValueEx(subkey, "InstallLocation")[0] + game_path = Path(install_location) / "Arknights.exe" if game_path.exists(): subprocess.Popen(str(game_path)) - self._log( - logging.INFO, - f"从注册表获取游戏路径并启动: {game_path}", - ) + self._log(logging.INFO, f"从注册表获取游戏路径并启动: {game_path}") break except: pass else: # 如果从注册表获取失败,使用默认路径 - game_path = Path( - "C:\\Program Files\\Arknights\\Arknights.exe" - ) + game_path = Path("C:\\Program Files\\Arknights\\Arknights.exe") if game_path.exists(): subprocess.Popen(str(game_path)) - self._log( - logging.INFO, - f"使用默认路径启动游戏: {game_path}", - ) + self._log(logging.INFO, f"使用默认路径启动游戏: {game_path}") else: self._log(logging.ERROR, "无法找到游戏可执行文件") return False except: # 如果注册表操作失败,使用默认路径 - game_path = Path( - "C:\\Program Files\\Arknights\\Arknights.exe" - ) + game_path = Path("C:\\Program Files\\Arknights\\Arknights.exe") if game_path.exists(): subprocess.Popen(str(game_path)) - self._log( - logging.INFO, f"使用默认路径启动游戏: {game_path}" - ) + self._log(logging.INFO, f"使用默认路径启动游戏: {game_path}") else: self._log(logging.ERROR, "无法找到游戏可执行文件") return False else: # 对于ADB端,重新启动游戏 - adb_path = getattr(self.connector, "adb_path", "adb") - subprocess.run( - f"{adb_path} -s {self.connector.device_serial} shell am start -n com.hypergryph.arknights/com.u8.sdk.U8UnityContext" - ) + adb_path = getattr(self.connector, 'adb_path', 'adb') + subprocess.run(f"{adb_path} -s {self.connector.device_serial} shell am start -n com.hypergryph.arknights/com.u8.sdk.U8UnityContext") self._log(logging.INFO, "重新启动游戏成功") except Exception as e: self._log(logging.ERROR, f"重新启动游戏失败: {e}") return False - + # 重新连接 try: self.connector.connect() @@ -244,19 +194,19 @@ def restart_game(self): self._log(logging.WARNING, "游戏重启后重新连接失败") except Exception as e: self._log(logging.ERROR, f"重新连接失败: {e}") - + return True def auto_login(self, first_start=False, stop_callback=None): """自动登录功能,处理服务器维护或掉线后的重新登录""" self._log(logging.INFO, "开始自动登录流程") - + def check_stop(): if stop_callback and not stop_callback(): self._log(logging.INFO, "检测到停止信号,中断登录流程") return False return True - + def sleep_with_check(seconds): """可中断的等待函数""" start_time = time.time() @@ -265,12 +215,9 @@ def sleep_with_check(seconds): return False time.sleep(0.1) return True - + # 确保连接器已连接 - if ( - not hasattr(self.connector, "is_connected") - or not self.connector.is_connected - ): + if not hasattr(self.connector, 'is_connected') or not self.connector.is_connected: logger.info("连接器未连接,尝试重新连接") try: self.connector.connect() @@ -280,29 +227,27 @@ def sleep_with_check(seconds): except Exception as e: logger.error(f"重新连接失败: {e}") return False - + # 首次启动时,先检测是否已经在游戏流程中 if first_start: logger.info("首次启动,检测是否已在游戏流程中") - + # 检查是否需要停止 if not check_stop(): return False - + screenshot = self.connector.capture_screenshot() if screenshot is not None: # 检查是否匹配到争锋频道入口 - matched, _ = self.match_template( - screenshot, "competition_page", threshold=0.7 - ) + matched, _ = self.match_template(screenshot, "competition_page", threshold=0.7) if matched: logger.info("已在争锋频道入口,无需登录") return True - + # 检查是否需要停止 if not check_stop(): return False - + # 检查是否匹配到0.png或1.png(加入赛事或开始游戏) try: # 尝试简单匹配 @@ -310,75 +255,53 @@ def sleep_with_check(seconds): # 检查是否需要停止 if not check_stop(): return False - - template_path = process_image_path(template_name) + + template_path = Path(f"images/process/{template_name}.png") if template_path.exists(): template = cv2.imread(str(template_path)) if template is not None: - screenshot_gray = cv2.cvtColor( - screenshot, cv2.COLOR_BGR2GRAY - ) - template_gray = cv2.cvtColor( - template, cv2.COLOR_BGR2GRAY - ) - res = cv2.matchTemplate( - screenshot_gray, - template_gray, - cv2.TM_CCOEFF_NORMED, - ) + screenshot_gray = cv2.cvtColor(screenshot, cv2.COLOR_BGR2GRAY) + template_gray = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY) + res = cv2.matchTemplate(screenshot_gray, template_gray, cv2.TM_CCOEFF_NORMED) _, max_val, _, _ = cv2.minMaxLoc(res) if max_val > 0.7: - logger.info( - f"已在争锋频道页面,找到模板 {template_name}.png" - ) + logger.info(f"已在争锋频道页面,找到模板 {template_name}.png") return True except Exception as e: logger.debug(f"检测争锋频道页面模板失败: {e}") - + # 检查是否需要停止 if not check_stop(): return False - + # 检查是否在战斗前准备阶段(PRE_BATTLE) # 匹配战斗前准备相关的模板(模板索引3,4,5,15) for template_name in ["3", "4", "5", "15"]: # 检查是否需要停止 if not check_stop(): return False - - template_path = process_image_path(template_name) + + template_path = Path(f"images/process/{template_name}.png") if template_path.exists(): try: template = cv2.imread(str(template_path)) if template is not None: - screenshot_gray = cv2.cvtColor( - screenshot, cv2.COLOR_BGR2GRAY - ) - template_gray = cv2.cvtColor( - template, cv2.COLOR_BGR2GRAY - ) - res = cv2.matchTemplate( - screenshot_gray, - template_gray, - cv2.TM_CCOEFF_NORMED, - ) + screenshot_gray = cv2.cvtColor(screenshot, cv2.COLOR_BGR2GRAY) + template_gray = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY) + res = cv2.matchTemplate(screenshot_gray, template_gray, cv2.TM_CCOEFF_NORMED) _, max_val, _, _ = cv2.minMaxLoc(res) if max_val > 0.7: - logger.info( - f"已在战斗前准备阶段,找到模板 {template_name}.png,无需登录" - ) + logger.info(f"已在战斗前准备阶段,找到模板 {template_name}.png,无需登录") return True except Exception as e: - logger.debug( - f"检测战斗前准备模板 {template_name}.png 失败: {e}" - ) - + logger.debug(f"检测战斗前准备模板 {template_name}.png 失败: {e}") + # 非首次启动时,等待游戏启动 if not first_start: self._log(logging.INFO, "重启游戏,等待游戏启动...") if not sleep_with_check(40): return False - + # 点击屏幕中心跳过中转页面(点击3次,每次间隔2秒) self._log(logging.INFO, "点击屏幕中心跳过中转页面") for i in range(3): @@ -387,7 +310,7 @@ def sleep_with_check(seconds): if i < 2: if not sleep_with_check(2): return False - + # 寻找并点击登录按钮 self._log(logging.INFO, "寻找登录按钮") login_button_found = False @@ -401,31 +324,26 @@ def sleep_with_check(seconds): if not sleep_with_check(1): return False continue - + h, w = screenshot.shape[:2] self._log(logging.DEBUG, f"截图尺寸: {w}x{h}") - - matched, pos = self.match_template( - screenshot, "login_button", threshold=0.9 - ) + + matched, pos = self.match_template(screenshot, "login_button", threshold=0.9) if matched: rel_x = pos[0] / w rel_y = pos[1] / h - self._log( - logging.INFO, - f"登录按钮位置: ({pos[0]}, {pos[1]}), 相对坐标: ({rel_x:.2f}, {rel_y:.2f})", - ) + self._log(logging.INFO, f"登录按钮位置: ({pos[0]}, {pos[1]}), 相对坐标: ({rel_x:.2f}, {rel_y:.2f})") self.connector.click((rel_x, rel_y)) self._log(logging.INFO, "点击登录按钮") login_button_found = True break if not sleep_with_check(1): return False - + if not login_button_found: self._log(logging.ERROR, "未找到登录按钮,登录流程中断") return False - + # 等待登录完成,最多等待70秒 self._log(logging.INFO, "等待登录完成 (最长70秒,动态检测)...") start_time = time.time() @@ -455,15 +373,13 @@ def sleep_with_check(seconds): # 寻找争锋频道入口,最多等待30秒 self._log(logging.INFO, "寻找争锋频道入口") start_time = time.time() - + # 尝试识别争锋频道 screenshot = self.connector.capture_screenshot() if screenshot is not None: - if self._check_and_click_competition_page( - screenshot, sleep_with_check, check_stop - ): + if self._check_and_click_competition_page(screenshot, sleep_with_check, check_stop): return True - + # 识别失败时点击右上角两次 self._log(logging.INFO, "未检测到争锋频道入口,点击屏幕右上角") for _ in range(2): @@ -474,9 +390,7 @@ def sleep_with_check(seconds): # 点击右上角后,立即再次检查争锋频道入口 screenshot = self.connector.capture_screenshot() if screenshot is not None: - if self._check_and_click_competition_page( - screenshot, sleep_with_check, check_stop - ): + if self._check_and_click_competition_page(screenshot, sleep_with_check, check_stop): return True # 同时检测两种关闭按钮五次(每次间隔2秒) @@ -509,9 +423,7 @@ def sleep_with_check(seconds): screenshot = self.connector.capture_screenshot() if screenshot is not None: - if self._check_and_click_competition_page( - screenshot, sleep_with_check, check_stop - ): + if self._check_and_click_competition_page(screenshot, sleep_with_check, check_stop): return True # 每次轮询间隔2秒,避免频繁截图造成性能浪费 @@ -521,22 +433,15 @@ def sleep_with_check(seconds): # 还是失败的话就重启 self._log(logging.ERROR, "未找到争锋频道入口,登录流程失败") return False - - def _check_and_click_competition_page( - self, screenshot, sleep_with_check, check_stop - ): + + def _check_and_click_competition_page(self, screenshot, sleep_with_check, check_stop): """争锋频道入口检测和点击方法""" - matched, pos = self.match_template( - screenshot, "competition_page", threshold=0.7 - ) + matched, pos = self.match_template(screenshot, "competition_page", threshold=0.7) if matched: h, w = screenshot.shape[:2] rel_x = pos[0] / w rel_y = pos[1] / h - self._log( - logging.INFO, - f"争锋频道入口位置: ({pos[0]}, {pos[1]}), 相对坐标: ({rel_x:.2f}, {rel_y:.2f})", - ) + self._log(logging.INFO, f"争锋频道入口位置: ({pos[0]}, {pos[1]}), 相对坐标: ({rel_x:.2f}, {rel_y:.2f})") self.connector.click((rel_x, rel_y)) self._log(logging.INFO, "点击进入争锋频道页面") if not sleep_with_check(2): @@ -544,7 +449,7 @@ def _check_and_click_competition_page( self._log(logging.INFO, "自动登录流程完成") return True return False - + def try_login_with_retry(self, max_wait_seconds=6, stop_callback=None): """尝试登录,如果未找到登录按钮则等待重试""" for i in range(max_wait_seconds): @@ -552,13 +457,11 @@ def try_login_with_retry(self, max_wait_seconds=6, stop_callback=None): if stop_callback and not stop_callback(): logger.info("检测到停止信号,取消登录尝试") return False - + screenshot = self.connector.capture_screenshot() if screenshot is not None: logger.info("获取截图成功,检查是否存在登录按钮") - matched, _ = self.match_template( - screenshot, "login_button", threshold=0.9 - ) + matched, _ = self.match_template(screenshot, "login_button", threshold=0.9) if matched: logger.info("找到登录按钮,执行登录流程") # 传递 stop_callback 给 auto_login @@ -569,7 +472,7 @@ def try_login_with_retry(self, max_wait_seconds=6, stop_callback=None): return False else: logger.info(f"第 {i+1} 次检查:未找到登录按钮,继续等待") - + # 可中断的等待 start_time = time.time() while time.time() - start_time < 6: @@ -578,32 +481,27 @@ def try_login_with_retry(self, max_wait_seconds=6, stop_callback=None): return False time.sleep(0.1) return False - + def restart_and_login(self, first_start=False, stop_callback=None): """重启游戏并尝试登录""" # 检查是否需要停止 if stop_callback and not stop_callback(): self._log(logging.INFO, "检测到停止信号,取消重启") return False - - self._log( - logging.INFO, - f"尝试重启游戏 (第 {self.restart_count + 1}/{self.max_restart_count} 次)", - ) + + self._log(logging.INFO, f"尝试重启游戏 (第 {self.restart_count + 1}/{self.max_restart_count} 次)") self.restart_count += 1 - + if self.restart_game(): self._log(logging.INFO, "游戏重启成功,尝试重新登录") - + # 检查是否需要停止 if stop_callback and not stop_callback(): self._log(logging.INFO, "检测到停止信号,取消登录") return False - + # 传递 stop_callback 给 auto_login - if self.auto_login( - first_start=first_start, stop_callback=stop_callback - ): + if self.auto_login(first_start=first_start, stop_callback=stop_callback): return True else: self._log(logging.ERROR, "重启后自动登录失败") @@ -611,26 +509,20 @@ def restart_and_login(self, first_start=False, stop_callback=None): else: self._log(logging.ERROR, "重启游戏失败") return False - + def auto_login_with_restart(self, first_start=False, stop_callback=None): """自动登录,失败时自动重启重试""" # 首先尝试直接登录(首次启动) - if self.auto_login( - first_start=first_start, stop_callback=stop_callback - ): + if self.auto_login(first_start=first_start, stop_callback=stop_callback): self.reset_restart_count() return True - + # 登录失败,尝试重启登录 for _ in range(self.max_restart_count - 1): - if not self.restart_and_login( - first_start=False, stop_callback=stop_callback - ): + if not self.restart_and_login(first_start=False, stop_callback=stop_callback): self._log(logging.WARNING, "重启登录失败,继续尝试") continue return True - - self._log( - logging.ERROR, f"已尝试 {self.max_restart_count} 次,登录失败" - ) - return False + + self._log(logging.ERROR, f"已尝试 {self.max_restart_count} 次,登录失败") + return False \ No newline at end of file diff --git a/src/game/maa_adb_connector.py b/maa_adb_connector.py similarity index 82% rename from src/game/maa_adb_connector.py rename to maa_adb_connector.py index 8c11a1c..b47bc9c 100644 --- a/src/game/maa_adb_connector.py +++ b/maa_adb_connector.py @@ -15,12 +15,9 @@ def resolve_maafw_path() -> str: if os.environ.get("MAAFW_BINARY_PATH"): return os.environ["MAAFW_BINARY_PATH"] candidates = [ - Path(sys.executable).parent / "vendor" / "bin" / "maafw", - Path.cwd() / "vendor" / "bin" / "maafw", - Path(__file__).resolve().parent.parent.parent - / "vendor" - / "bin" - / "maafw", + Path(sys.executable).parent / "maafw", + Path.cwd() / "maafw", + Path(__file__).resolve().parent / "maafw", ] for p in candidates: if p.is_dir() and any(p.glob("MaaFramework.dll")): @@ -58,7 +55,7 @@ class InputMethodOption: @dataclass(frozen=True) class MaaConnectionConfig: maa_binary_path: str = "" - adb_path: str = r".\vendor\bin\platform-tools\adb.exe" + adb_path: str = r".\platform-tools\adb.exe" device_serial: str = "" screencap_method: int = 1 input_method: int = 4 @@ -142,30 +139,12 @@ def reset(cls): class ConnectionTypeRegistry: _types: list[ConnectionType] = [ - ConnectionType( - "adb", "ADB连接", "", "通用ADB连接,需手动指定设备地址" - ), - ConnectionType( - "ldplayer", "雷电模拟器", "emulator-5554", "雷电模拟器默认ADB地址" - ), - ConnectionType( - "mumu", "MuMu模拟器", "127.0.0.1:7555", "MuMu模拟器默认ADB地址" - ), - ConnectionType( - "mumu12", - "MuMu12模拟器", - "127.0.0.1:16384", - "MuMu12模拟器默认ADB地址", - ), - ConnectionType( - "bluestacks", - "蓝叠模拟器", - "127.0.0.1:5555", - "蓝叠模拟器默认ADB地址", - ), - ConnectionType( - "nox", "夜神模拟器", "127.0.0.1:62001", "夜神模拟器默认ADB地址" - ), + ConnectionType("adb", "ADB连接", "", "通用ADB连接,需手动指定设备地址"), + ConnectionType("ldplayer", "雷电模拟器", "emulator-5554", "雷电模拟器默认ADB地址"), + ConnectionType("mumu", "MuMu模拟器", "127.0.0.1:7555", "MuMu模拟器默认ADB地址"), + ConnectionType("mumu12", "MuMu12模拟器", "127.0.0.1:16384", "MuMu12模拟器默认ADB地址"), + ConnectionType("bluestacks", "蓝叠模拟器", "127.0.0.1:5555", "蓝叠模拟器默认ADB地址"), + ConnectionType("nox", "夜神模拟器", "127.0.0.1:62001", "夜神模拟器默认ADB地址"), ] @classmethod @@ -189,24 +168,10 @@ def get_type_by_id(cls, type_id: str) -> ConnectionType | None: class InputMethodRegistry: _methods: list[InputMethodOption] = [ - InputMethodOption( - "adb_shell", 1, "AdbShell", "ADB shell input命令,兼容性最高" - ), - InputMethodOption( - "minitouch_adb_key", - 2, - "MinitouchAndAdbKey", - "minitouch注入+ADB按键,低延迟需root", - ), - InputMethodOption( - "maatouch", 4, "Maatouch", "Maatouch注入,低延迟MAA自带" - ), - InputMethodOption( - "emulator_extras", - 8, - "EmulatorExtras", - "模拟器扩展接口,仅特定模拟器支持", - ), + InputMethodOption("adb_shell", 1, "AdbShell", "ADB shell input命令,兼容性最高"), + InputMethodOption("minitouch_adb_key", 2, "MinitouchAndAdbKey", "minitouch注入+ADB按键,低延迟需root"), + InputMethodOption("maatouch", 4, "Maatouch", "Maatouch注入,低延迟MAA自带"), + InputMethodOption("emulator_extras", 8, "EmulatorExtras", "模拟器扩展接口,仅特定模拟器支持"), ] @classmethod @@ -258,9 +223,7 @@ def connect(self): Toolkit.init_option(str(Path.cwd())) - target_serial = ( - self.device_serial if self.device_serial else "127.0.0.1:5555" - ) + target_serial = self.device_serial if self.device_serial else "127.0.0.1:5555" self.device_serial = target_serial adb_path = str(Path(self._config.adb_path).resolve()) @@ -273,24 +236,18 @@ def connect(self): ) self._ctrl.post_connection().wait() - self._ctrl.set_screenshot_use_raw_size( - self._config.screenshot_use_raw_size - ) + self._ctrl.set_screenshot_use_raw_size(self._config.screenshot_use_raw_size) self._ctrl.post_screencap().wait() image = self._ctrl.cached_image if image is not None: self.screen_height, self.screen_width = image.shape[:2] else: - self.screen_width, self.screen_height = ( - self._get_window_size_fallback() - ) + self.screen_width, self.screen_height = self._get_window_size_fallback() self.is_maa_available = True self.is_connected = True - logger.info( - f"MAA Framework ADB连接成功: {target_serial}, 分辨率: {self.screen_width}x{self.screen_height}" - ) + logger.info(f"MAA Framework ADB连接成功: {target_serial}, 分辨率: {self.screen_width}x{self.screen_height}") except Exception as e: self.is_maa_available = False @@ -300,14 +257,7 @@ def connect(self): def _get_window_size_fallback(self) -> tuple[int, int]: try: size_cmd = f"{self._config.adb_path} -s {self.device_serial} shell wm size" - result = subprocess.run( - size_cmd, - shell=True, - capture_output=True, - text=True, - check=True, - timeout=5, - ) + result = subprocess.run(size_cmd, shell=True, capture_output=True, text=True, check=True, timeout=5) output = result.stdout.strip() if "Physical size:" in output: res_str = output.split("Physical size: ")[1] @@ -346,12 +296,7 @@ def click(self, point: tuple[float, float]): except Exception as e: logger.error(f"MAA点击失败: {e}") - def swipe( - self, - start: tuple[float, float], - end: tuple[float, float], - duration: int = 500, - ): + def swipe(self, start: tuple[float, float], end: tuple[float, float], duration: int = 500): if not self.is_connected or self._ctrl is None: return try: @@ -361,19 +306,14 @@ def swipe( y1_coord = int(y1 * self.screen_height) x2_coord = int(x2 * self.screen_width) y2_coord = int(y2 * self.screen_height) - logger.info( - f"MAA滑动: ({x1_coord},{y1_coord}) -> ({x2_coord},{y2_coord})" - ) - self._ctrl.post_swipe( - x1_coord, y1_coord, x2_coord, y2_coord, duration - ).wait() + logger.info(f"MAA滑动: ({x1_coord},{y1_coord}) -> ({x2_coord},{y2_coord})") + self._ctrl.post_swipe(x1_coord, y1_coord, x2_coord, y2_coord, duration).wait() except Exception as e: logger.error(f"MAA滑动失败: {e}") def get_device_list(self) -> list[str]: try: from maa.toolkit import AdbDevice - devices = AdbDevice.find() if devices: return [d.name for d in devices] @@ -381,13 +321,7 @@ def get_device_list(self) -> list[str]: logger.debug("MAA AdbDevice.find()失败,降级到subprocess") try: device_cmd = f"{self._config.adb_path} devices" - result = subprocess.run( - device_cmd, - shell=True, - capture_output=True, - text=True, - timeout=5, - ) + result = subprocess.run(device_cmd, shell=True, capture_output=True, text=True, timeout=5) devices = [] for line in result.stdout.split("\n"): if "\tdevice" in line: @@ -417,10 +351,9 @@ def set_config(self, config: MaaConnectionConfig): class AdbConnectorAdapter: - def __init__(self, adb_path: str = r".\vendor\bin\platform-tools\adb.exe"): - from src.data import load_data - - self._legacy_connector = load_data.AdbConnector() + def __init__(self, adb_path: str = r".\platform-tools\adb.exe"): + import loadData + self._legacy_connector = loadData.AdbConnector() self._maa_connector: MaaAdbConnector | None = None self._use_maa: bool = False self._maa_config = MaaConnectionConfig(adb_path=adb_path) @@ -523,10 +456,7 @@ def connect(self): if MaaFrameworkDetector.is_available(): try: maa_connector = MaaAdbConnector(self._maa_config) - maa_connector.device_serial = ( - self._legacy_connector.device_serial - or self._maa_config.device_serial - ) + maa_connector.device_serial = self._legacy_connector.device_serial or self._maa_config.device_serial maa_connector.connect() if maa_connector.is_connected: self._maa_connector = maa_connector @@ -557,12 +487,7 @@ def click(self, point: tuple[float, float]): else: self._legacy_connector.click(point) - def swipe( - self, - start: tuple[float, float], - end: tuple[float, float], - duration: int = 500, - ): + def swipe(self, start: tuple[float, float], end: tuple[float, float], duration: int = 500): if self._use_maa and self._maa_connector: self._maa_connector.swipe(start, end, duration) else: diff --git a/vendor/bin/maafw/DirectML.dll b/maafw/DirectML.dll similarity index 100% rename from vendor/bin/maafw/DirectML.dll rename to maafw/DirectML.dll diff --git a/vendor/bin/maafw/MaaAdbControlUnit.dll b/maafw/MaaAdbControlUnit.dll similarity index 100% rename from vendor/bin/maafw/MaaAdbControlUnit.dll rename to maafw/MaaAdbControlUnit.dll diff --git a/vendor/bin/maafw/MaaAgentClient.dll b/maafw/MaaAgentClient.dll similarity index 100% rename from vendor/bin/maafw/MaaAgentClient.dll rename to maafw/MaaAgentClient.dll diff --git a/vendor/bin/maafw/MaaAgentServer.dll b/maafw/MaaAgentServer.dll similarity index 100% rename from vendor/bin/maafw/MaaAgentServer.dll rename to maafw/MaaAgentServer.dll diff --git a/vendor/bin/maafw/MaaCustomControlUnit.dll b/maafw/MaaCustomControlUnit.dll similarity index 100% rename from vendor/bin/maafw/MaaCustomControlUnit.dll rename to maafw/MaaCustomControlUnit.dll diff --git a/vendor/bin/maafw/MaaFramework.dll b/maafw/MaaFramework.dll similarity index 100% rename from vendor/bin/maafw/MaaFramework.dll rename to maafw/MaaFramework.dll diff --git a/vendor/bin/maafw/MaaGamepadControlUnit.dll b/maafw/MaaGamepadControlUnit.dll similarity index 100% rename from vendor/bin/maafw/MaaGamepadControlUnit.dll rename to maafw/MaaGamepadControlUnit.dll diff --git a/vendor/bin/maafw/MaaNode.node b/maafw/MaaNode.node similarity index 100% rename from vendor/bin/maafw/MaaNode.node rename to maafw/MaaNode.node diff --git a/vendor/bin/maafw/MaaNodeServer.node b/maafw/MaaNodeServer.node similarity index 100% rename from vendor/bin/maafw/MaaNodeServer.node rename to maafw/MaaNodeServer.node diff --git a/vendor/bin/maafw/MaaPiCli.exe b/maafw/MaaPiCli.exe similarity index 100% rename from vendor/bin/maafw/MaaPiCli.exe rename to maafw/MaaPiCli.exe diff --git a/vendor/bin/maafw/MaaRecordControlUnit.dll b/maafw/MaaRecordControlUnit.dll similarity index 100% rename from vendor/bin/maafw/MaaRecordControlUnit.dll rename to maafw/MaaRecordControlUnit.dll diff --git a/vendor/bin/maafw/MaaReplayControlUnit.dll b/maafw/MaaReplayControlUnit.dll similarity index 100% rename from vendor/bin/maafw/MaaReplayControlUnit.dll rename to maafw/MaaReplayControlUnit.dll diff --git a/vendor/bin/maafw/MaaToolkit.dll b/maafw/MaaToolkit.dll similarity index 100% rename from vendor/bin/maafw/MaaToolkit.dll rename to maafw/MaaToolkit.dll diff --git a/vendor/bin/maafw/MaaUtils.dll b/maafw/MaaUtils.dll similarity index 100% rename from vendor/bin/maafw/MaaUtils.dll rename to maafw/MaaUtils.dll diff --git a/vendor/bin/maafw/MaaWin32ControlUnit.dll b/maafw/MaaWin32ControlUnit.dll similarity index 100% rename from vendor/bin/maafw/MaaWin32ControlUnit.dll rename to maafw/MaaWin32ControlUnit.dll diff --git a/vendor/bin/maafw/ViGEmClient.dll b/maafw/ViGEmClient.dll similarity index 100% rename from vendor/bin/maafw/ViGEmClient.dll rename to maafw/ViGEmClient.dll diff --git a/vendor/bin/maafw/fastdeploy_ppocr_maa.dll b/maafw/fastdeploy_ppocr_maa.dll similarity index 100% rename from vendor/bin/maafw/fastdeploy_ppocr_maa.dll rename to maafw/fastdeploy_ppocr_maa.dll diff --git a/vendor/bin/maafw/onnxruntime_maa.dll b/maafw/onnxruntime_maa.dll similarity index 100% rename from vendor/bin/maafw/onnxruntime_maa.dll rename to maafw/onnxruntime_maa.dll diff --git a/vendor/bin/maafw/opencv_world4_maa.dll b/maafw/opencv_world4_maa.dll similarity index 100% rename from vendor/bin/maafw/opencv_world4_maa.dll rename to maafw/opencv_world4_maa.dll diff --git a/vendor/bin/maafw/plugins/MaaPluginDemo.dll b/maafw/plugins/MaaPluginDemo.dll similarity index 100% rename from vendor/bin/maafw/plugins/MaaPluginDemo.dll rename to maafw/plugins/MaaPluginDemo.dll diff --git a/main.py b/main.py index af13f4c..85a35e4 100644 --- a/main.py +++ b/main.py @@ -8,54 +8,27 @@ import numpy as np from pathlib import Path import onnxruntime # workaround: Pre-import to avoid ImportError: DLL load failed while importing onnxruntime_pybind11_state: 动态链接库(DLL)初始化例程失败。 -from PyQt6.QtWidgets import ( - QApplication, - QMainWindow, - QWidget, - QVBoxLayout, - QHBoxLayout, -) -from PyQt6.QtWidgets import ( - QLabel, - QPushButton, - QLineEdit, - QCheckBox, - QComboBox, - QButtonGroup, -) -from PyQt6.QtWidgets import ( - QGroupBox, - QMessageBox, - QGraphicsDropShadowEffect, - QFrame, -) -from PyQt6.QtCore import ( - Qt, - pyqtSignal, - QThread, - QPropertyAnimation, - QEasingCurve, -) +from PyQt6.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout +from PyQt6.QtWidgets import QLabel, QPushButton, QLineEdit, QCheckBox, QComboBox, QButtonGroup +from PyQt6.QtWidgets import QGroupBox, QMessageBox, QGraphicsDropShadowEffect, QFrame +from PyQt6.QtCore import Qt, pyqtSignal, QThread, QPropertyAnimation, QEasingCurve from PyQt6.QtGui import QPixmap, QFont, QIcon, QPainter, QColor import PyQt6.QtCore as QtCore -from src.data import load_data -from src.game import auto_fetch -from src.game.maa_adb_connector import ( - AdbConnectorAdapter, - ConnectionTypeRegistry, - InputMethodRegistry, -) -from src.ui.dark_mode_style_fix import DarkModeStyleFix -from src.analysis import similar_history_match -from src.recognition import recognize -from src.recognition.recognize import MONSTER_COUNT -from src.recognition.specialmonster import SpecialMonsterHandler -from src.data import data_package -from src.game import winrt_capture -from src.core.config import MONSTER_DATA -from src.ui.similar_history_match_ui import HistoryMatchUI -from src.ui.input_panel_ui import InputPanelUI +import loadData +import auto_fetch +import maa_adb_connector +from maa_adb_connector import AdbConnectorAdapter, ConnectionTypeRegistry, InputMethodRegistry, MaaFrameworkDetector +from dark_mode_style_fix import DarkModeStyleFix +import similar_history_match +import recognize +from recognize import MONSTER_COUNT +from specialmonster import SpecialMonsterHandler +import data_package +import winrt_capture +from config import FIELD_FEATURE_COUNT, MONSTER_DATA +from simular_history_match_ui import HistoryMatchUI +from input_panel_ui import InputPanelUI logging.getLogger().setLevel(logging.DEBUG) logging.getLogger("PIL").setLevel(logging.INFO) @@ -70,18 +43,19 @@ try: - from src.models.predict import CannotModel + from predict import CannotModel + from train import UnitAwareTransformer logger.info("Using PyTorch model for predictions.") except: - from src.models.predict_onnx import CannotModel + from predict_onnx import CannotModel logger.info("Using ONNX model for predictions.") class ADBConnectorThread(QThread): """ - Worker thread to run load_data.AdbConnector.connect() without blocking the UI. + Worker thread to run loadData.AdbConnector.connect() without blocking the UI. """ connect_finished = pyqtSignal() @@ -94,7 +68,6 @@ def run(self): self.app.adb_connector.connect() self.connect_finished.emit() - class ArknightsApp(QMainWindow): # 添加自定义信号 update_button_signal = pyqtSignal(str) # 用于更新按钮文本 @@ -125,11 +98,9 @@ def __init__(self): # 尝试连接模拟器 self.adb_connector = AdbConnectorAdapter() - self.pc_connector = load_data.PcConnector() + self.pc_connector = loadData.PcConnector() self.adb_connector_thread = ADBConnectorThread(self) - self.adb_connector_thread.connect_finished.connect( - self.on_adb_connected - ) + self.adb_connector_thread.connect_finished.connect(self.on_adb_connected) self.adb_connector_thread.start() self.auto_fetch_running = False @@ -148,16 +119,10 @@ def __init__(self): self.history_match = similar_history_match.HistoryMatch() # Ensure feat_past and N_history are initialized try: - self.history_match.feat_past = np.hstack( - [self.history_match.past_left, self.history_match.past_right] - ) + self.history_match.feat_past = np.hstack([self.history_match.past_left, self.history_match.past_right]) except Exception: self.history_match.feat_past = None - self.history_match.N_history = ( - 0 - if self.history_match.labels is None - else len(self.history_match.labels) - ) + self.history_match.N_history = 0 if self.history_match.labels is None else len(self.history_match.labels) logger.info("错题本加载成功") # 初始化特殊怪物语言触发处理程序 @@ -170,9 +135,7 @@ def __init__(self): self.recognize_button.setEnabled(False) self.recognize_button.setToolTip("模型未加载,无法使用此功能") self.input_panel.predict_button.setEnabled(False) - self.input_panel.predict_button.setToolTip( - "模型未加载,无法使用此功能" - ) + self.input_panel.predict_button.setToolTip("模型未加载,无法使用此功能") def init_ui(self): try: @@ -181,19 +144,13 @@ def init_ui(self): version = pyproject_data["project"]["version"] except (FileNotFoundError, KeyError): version = "unknown" - model_name = ( - Path(self.cannot_model.model_path).name - if self.cannot_model.model_path - else "未加载" - ) - self.setWindowTitle( - f"铁鲨鱼_Arknights Neural Network - v{version} - model: {model_name}" - ) - self.setWindowIcon(QIcon("src/resources/assets/icons/icon.ico")) + model_name = Path(self.cannot_model.model_path).name if self.cannot_model.model_path else "未加载" + self.setWindowTitle(f"铁鲨鱼_Arknights Neural Network - v{version} - model: {model_name}") + self.setWindowIcon(QIcon("ico/icon.ico")) self.setGeometry(100, 100, 500, 580) self.setMinimumWidth(580) self.setMaximumWidth(580) - self.background = QPixmap("src/resources/assets/icons/background.png") + self.background = QPixmap("ico/background.png") # 初始化动画对象 self.size_animation = QPropertyAnimation(self, b"size") @@ -220,7 +177,8 @@ def init_ui(self): # 顶部区域 - 输入显示 input_display = QGroupBox() - input_display.setStyleSheet(""" + input_display.setStyleSheet( + """ QGroupBox { background-color: rgba(0, 0, 0, 120); border-radius: 15px; @@ -234,7 +192,8 @@ def init_ui(self): left: 15px; padding: 0 5px; } - """) + """ + ) input_layout = QHBoxLayout(input_display) # 左侧人物显示 @@ -261,13 +220,15 @@ def init_ui(self): # 中部区域 - 预测结果 result_group = QGroupBox() - result_group.setStyleSheet(""" + result_group.setStyleSheet( + """ QGroupBox { background-color: rgba(120, 120, 120, 10); border-radius: 15px; border: 1px solid #747474; } - """) + """ + ) result_layout = QVBoxLayout(result_group) result_layout.setSpacing(10) result_layout.setContentsMargins(10, 10, 10, 10) @@ -279,16 +240,10 @@ def init_ui(self): result_layout.addWidget(self.result_label) # 添加模型名称显示 - model_name = ( - Path(self.cannot_model.model_path).name - if self.cannot_model.model_path - else "未加载" - ) + model_name = Path(self.cannot_model.model_path).name if self.cannot_model.model_path else "未加载" self.model_name_label = QLabel(f"model: {model_name}") self.model_name_label.setFont(QFont("Microsoft YaHei", 8)) - self.model_name_label.setAlignment( - Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignBottom - ) + self.model_name_label.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignBottom) self.model_name_label.setStyleSheet("color: #666666;") result_layout.addWidget(self.model_name_label) @@ -430,9 +385,7 @@ def init_ui(self): self.connection_type_combo = QComboBox() for ct in ConnectionTypeRegistry.get_all_types(): self.connection_type_combo.addItem(ct.display_name, ct.type_id) - self.connection_type_combo.currentIndexChanged.connect( - self.on_connection_type_changed - ) + self.connection_type_combo.currentIndexChanged.connect(self.on_connection_type_changed) self.input_method_label = QLabel("输入方式:") self.input_method_combo = QComboBox() @@ -442,9 +395,7 @@ def init_ui(self): idx = self.input_method_combo.findData(default_method.method_id) if idx >= 0: self.input_method_combo.setCurrentIndex(idx) - self.input_method_combo.currentIndexChanged.connect( - self.on_input_method_changed - ) + self.input_method_combo.currentIndexChanged.connect(self.on_input_method_changed) maa_row_layout.addWidget(self.connection_type_label) maa_row_layout.addWidget(self.connection_type_combo) @@ -580,9 +531,7 @@ def animate_size_change(self, target_width, target_height=None): self.setMaximumWidth(max(self.width(), target_width)) self.size_animation.setStartValue(self.size()) - self.size_animation.setEndValue( - QtCore.QSize(target_width, target_height) - ) + self.size_animation.setEndValue(QtCore.QSize(target_width, target_height)) self.size_animation.start() def set_fixed_after_animation(): @@ -601,14 +550,14 @@ def on_mode_changed(self, mode): self.current_capture_mode = mode logger.info(f"切换捕获模式为: {mode}") - is_win_mode = mode == "WIN" - is_adb_mode = mode == "ADB" - is_pc_mode = mode == "PC" + is_win_mode = (mode == "WIN") + is_adb_mode = (mode == "ADB") + is_pc_mode = (mode == "PC") # 切换窗口捕获相关控件 self.choose_window_button.setEnabled(is_win_mode) self.reselect_button.setEnabled(is_win_mode) - + # 切换 ADB 相关控件 self.serial_label.setEnabled(is_adb_mode) self.serial_entry.setEnabled(is_adb_mode) @@ -629,30 +578,20 @@ def on_mode_changed(self, mode): if self.recognizer._winrt is None: self.choose_capture_window() elif mode == "PC": - self.recognizer = recognize.RecognizeMonster( - method="ADB" - ) # reuse ADB reading methodology but on PC Connector + self.recognizer = recognize.RecognizeMonster(method="ADB") # reuse ADB reading methodology but on PC Connector if not self.pc_connector.is_connected: self.pc_connector.connect() if not self.pc_connector.is_connected: - QMessageBox.warning( - self, "警告", "未能连接到PC端窗口(明日方舟)。" - ) + QMessageBox.warning(self, "警告", "未能连接到PC端窗口(明日方舟)。") def on_adb_connected(self): logger.info("模拟器初始化完成") if self.adb_connector.is_maa_available: self.maa_status_label.setText("MAA Framework已连接") - self.maa_status_label.setStyleSheet( - "color: #00aa00; font-size: 10px;" - ) + self.maa_status_label.setStyleSheet("color: #00aa00; font-size: 10px;") else: - self.maa_status_label.setText( - "使用自有ADB实现(MAA Framework不可用)" - ) - self.maa_status_label.setStyleSheet( - "color: #996600; font-size: 10px;" - ) + self.maa_status_label.setText("使用自有ADB实现(MAA Framework不可用)") + self.maa_status_label.setStyleSheet("color: #996600; font-size: 10px;") def on_connection_type_changed(self, index): type_id = self.connection_type_combo.currentData() @@ -666,9 +605,7 @@ def on_connection_type_changed(self, index): if self.adb_connector.is_connected: self.adb_connector.disconnect() self.maa_status_label.setText("已断开,请重新连接") - self.maa_status_label.setStyleSheet( - "color: #aa0000; font-size: 10px;" - ) + self.maa_status_label.setStyleSheet("color: #aa0000; font-size: 10px;") def on_input_method_changed(self, index): method_id = self.input_method_combo.currentData() @@ -676,9 +613,7 @@ def on_input_method_changed(self, index): return self.adb_connector.set_input_method(method_id) if self.adb_connector.is_connected: - QMessageBox.information( - self, "提示", "输入方式已更改,请重新连接以生效" - ) + QMessageBox.information(self, "提示", "输入方式已更改,请重新连接以生效") def choose_capture_window(self): """弹出窗口选择器,切换 WinRT 截屏源(窗口标题或整屏)。""" @@ -702,32 +637,20 @@ def choose_capture_window(self): return hint = "" if "window_name" in sel: - self.recognizer = recognize.RecognizeMonster( - method="WIN", - window_name=sel["window_name"], - monitor_index=None, - ) + self.recognizer = recognize.RecognizeMonster(method="WIN", window_name=sel["window_name"], monitor_index=None) hint = f"已切换至窗口:{sel['window_name']}" else: idx = max(1, sel["monitor_index"]) - self.recognizer = recognize.RecognizeMonster( - method="WIN", window_name=None, monitor_index=idx - ) + self.recognizer = recognize.RecognizeMonster(method="WIN", window_name=None, monitor_index=idx) hint = f"已切换至整屏:显示器 {sel['monitor_index']}" self.no_region = True - QMessageBox.information( - self, "成功", hint + "\n建议重新选择范围。" - ) + QMessageBox.information(self, "成功", hint + "\n建议重新选择范围。") except Exception as e: - QMessageBox.critical( - self, "异常", f"{e}\n\n{traceback.format_exc()}" - ) + QMessageBox.critical(self, "异常", f"{e}\n\n{traceback.format_exc()}") finally: self._switching_source = False - self.choose_window_button.setEnabled( - self.current_capture_mode == "WIN" - ) + self.choose_window_button.setEnabled(self.current_capture_mode == "WIN") def paintEvent(self, event): painter = QPainter(self) @@ -745,9 +668,7 @@ def paintEvent(self, event): ) def update_input_display(self): - left_monsters_dict, right_monsters_dict = ( - self.input_panel.get_monster_counts() - ) + left_monsters_dict, right_monsters_dict = self.input_panel.get_monster_counts() def update_input_display_half(input_layout, monsters_dict): # 清除现有显示 @@ -760,9 +681,7 @@ def update_input_display_half(input_layout, monsters_dict): value = monsters_dict[str(i)].text() if value.isdigit() and int(value) > 0: has_input = True - monster_widget = self.create_monster_display_widget( - i, value - ) + monster_widget = self.create_monster_display_widget(i, value) input_layout.addWidget(monster_widget) # 如果没有输入,显示提示 if not has_input: @@ -781,11 +700,13 @@ def create_monster_display_widget(self, monster_id, count): shadow.setOffset(2) # 偏移量(0表示均匀四周发光) widget.setGraphicsEffect(shadow) - widget.setStyleSheet(""" + widget.setStyleSheet( + """ QWidget { border-radius: 0px; } - """) + """ + ) layout = QVBoxLayout(widget) layout.setSpacing(2) @@ -798,15 +719,10 @@ def create_monster_display_widget(self, monster_id, count): img_label.setAlignment(Qt.AlignmentFlag.AlignCenter) try: - pixmap = QPixmap( - f"src/resources/assets/images/{MONSTER_DATA['原始名称'][monster_id]}.png" - ) + pixmap = QPixmap(f"images/{MONSTER_DATA['原始名称'][monster_id]}.png") if not pixmap.isNull(): pixmap = pixmap.scaled( - 70, - 70, - Qt.AspectRatioMode.KeepAspectRatio, - Qt.TransformationMode.SmoothTransformation, + 70, 70, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation ) img_label.setPixmap(pixmap) except Exception as e: @@ -824,13 +740,15 @@ def create_monster_display_widget(self, monster_id, count): # 数量标签 count_label = QLabel(count) count_label.setAlignment(Qt.AlignmentFlag.AlignCenter) - count_label.setStyleSheet(""" + count_label.setStyleSheet( + """ color: #EDEDED; font: bold 20px SimHei; border-radius: 5px; padding: 2px 5px; min-width: 20px; - """) + """ + ) layout.addWidget(img_label) layout.addWidget(count_label) @@ -844,40 +762,28 @@ def reset_entries(self): def get_prediction(self): try: - left_monsters_dict, right_monsters_dict = ( - self.input_panel.get_monster_counts() - ) + left_monsters_dict, right_monsters_dict = self.input_panel.get_monster_counts() left_counts = np.zeros(MONSTER_COUNT, dtype=np.int16) right_counts = np.zeros(MONSTER_COUNT, dtype=np.int16) for name, entry in left_monsters_dict.items(): value = entry.text() - left_counts[int(name) - 1] = ( - int(value) if value.isdigit() else 0 - ) + left_counts[int(name) - 1] = int(value) if value.isdigit() else 0 for name, entry in right_monsters_dict.items(): value = entry.text() - right_counts[int(name) - 1] = ( - int(value) if value.isdigit() else 0 - ) + right_counts[int(name) - 1] = int(value) if value.isdigit() else 0 # 构建包含地形的完整特征向量 - full_features = self.input_panel.build_terrain_features( - left_counts, right_counts - ) + full_features = self.input_panel.build_terrain_features(left_counts, right_counts) - prediction = self.cannot_model.get_prediction_with_terrain( - full_features - ) + prediction = self.cannot_model.get_prediction_with_terrain(full_features) return prediction except FileNotFoundError: QMessageBox.critical(self, "错误", "未找到模型文件,请先训练") except RuntimeError as e: if "size mismatch" in str(e): - QMessageBox.critical( - self, "错误", "模型结构不匹配!请删除旧模型并重新训练" - ) + QMessageBox.critical(self, "错误", "模型结构不匹配!请删除旧模型并重新训练") else: QMessageBox.critical(self, "错误", f"模型加载失败: {str(e)}") except ValueError: @@ -904,20 +810,13 @@ def update_prediction(self, prediction): else: self.result_label.setStyleSheet("color: #25ace2; font: bold,14px;") - left_monsters_dict, right_monsters_dict = ( - self.input_panel.get_monster_counts() - ) + left_monsters_dict, right_monsters_dict = self.input_panel.get_monster_counts() # 生成结果文本 if winner != "难说": - result_text = ( - f"预测胜方: {winner}\n" - f"左 {left_win_prob:.2%} | 右 {right_win_prob:.2%}\n" - ) + result_text = f"预测胜方: {winner}\n" f"左 {left_win_prob:.2%} | 右 {right_win_prob:.2%}\n" else: result_text = ( - f"这一把{winner}\n" - f"左 {left_win_prob:.2%} | 右 {right_win_prob:.2%}\n" - f"难道说?难道说?难道说?\n" + f"这一把{winner}\n" f"左 {left_win_prob:.2%} | 右 {right_win_prob:.2%}\n" f"难道说?难道说?难道说?\n" ) self.result_label.setStyleSheet("color: black; font: bold,24px;") @@ -936,12 +835,8 @@ def predict(self): self.update_input_display() if self.history_match_ui.isVisible(): - left_monsters_dict, right_monsters_dict = ( - self.input_panel.get_monster_counts() - ) - self.history_match_ui.render_similar_matches( - left_monsters_dict, right_monsters_dict - ) + left_monsters_dict, right_monsters_dict = self.input_panel.get_monster_counts() + self.history_match_ui.render_similar_matches(left_monsters_dict, right_monsters_dict) def get_recognize(self): """ @@ -956,7 +851,7 @@ def get_recognize(self): screenshot = self.active_connector.capture_screenshot() if screenshot is None: logger.error(f"{self.current_capture_mode} 截图失败") - + results = self.recognizer.process_regions(screenshot) else: # WIN 模式,recognizer 内部处理 WinRT 或 PIL @@ -993,32 +888,22 @@ def recognize_and_predict(self): self.update_prediction(prediction) # 历史对局 if self.history_match_ui.isVisible(): - left_monsters_dict, right_monsters_dict = ( - self.input_panel.get_monster_counts() - ) - self.history_match_ui.render_similar_matches( - left_monsters_dict, right_monsters_dict - ) + left_monsters_dict, right_monsters_dict = self.input_panel.get_monster_counts() + self.history_match_ui.render_similar_matches(left_monsters_dict, right_monsters_dict) def toggle_history_panel(self): """切换历史对局面板的显示""" target_width = self.width() if self.history_match is None: - QMessageBox.warning( - self, "警告", "历史数据加载失败,无法显示历史对局" - ) + QMessageBox.warning(self, "警告", "历史数据加载失败,无法显示历史对局") return is_visible = self.history_match_ui.isVisible() self.history_match_ui.setVisible(not is_visible) if not is_visible: self.history_button.setText("隐藏历史对局") - left_monsters_dict, right_monsters_dict = ( - self.input_panel.get_monster_counts() - ) - self.history_match_ui.render_similar_matches( - left_monsters_dict, right_monsters_dict - ) + left_monsters_dict, right_monsters_dict = self.input_panel.get_monster_counts() + self.history_match_ui.render_similar_matches(left_monsters_dict, right_monsters_dict) target_width += 540 else: self.history_button.setText("显示历史对局") @@ -1029,9 +914,7 @@ def reselect_roi(self): self.recognizer.select_roi() def toggle_auto_fetch(self): - if not ( - hasattr(self, "auto_fetch") and self.auto_fetch.auto_fetch_running - ): + if not (hasattr(self, "auto_fetch") and self.auto_fetch.auto_fetch_running): self.auto_fetch = auto_fetch.AutoFetch( self.active_connector, self.game_mode, @@ -1041,8 +924,7 @@ def toggle_auto_fetch(self): updater=self.update_statistics_callback, start_callback=self.start_callback, stop_callback=self.stop_callback, - training_duration=float(self.duration_entry.text()) - * 3600, # 获取训练时长 + training_duration=float(self.duration_entry.text()) * 3600, # 获取训练时长 recognizer=self.recognizer, cannot_model=self.cannot_model, ) @@ -1051,11 +933,7 @@ def toggle_auto_fetch(self): self.auto_fetch.stop_auto_fetch() def update_statistics(self): - elapsed_time = ( - time.time() - self.auto_fetch.start_time - if self.auto_fetch.start_time - else 0 - ) + elapsed_time = time.time() - self.auto_fetch.start_time if self.auto_fetch.start_time else 0 hours, remainder = divmod(elapsed_time, 3600) minutes, _ = divmod(remainder, 60) stats_text = ( @@ -1078,18 +956,14 @@ def refresh_device_list(self): self.serial_entry.setCurrentIndex(0) else: self.serial_entry.addItem("127.0.0.1:5555") - self.serial_entry.setCurrentText( - current_text if current_text else "127.0.0.1:5555" - ) + self.serial_entry.setCurrentText(current_text if current_text else "127.0.0.1:5555") def update_device_serial(self): new_serial = self.serial_entry.currentText() device_serial = self.adb_connector.update_device_serial(new_serial) self.adb_connector.connect() # 尝试连接新设备 self.serial_entry.setCurrentText(device_serial) - QMessageBox.information( - self, "提示", f"已更新模拟器序列号为: {device_serial}" - ) + QMessageBox.information(self, "提示", f"已更新模拟器序列号为: {device_serial}") def start_callback(self): self.update_button_signal.emit("停止自动获取数据") @@ -1113,9 +987,7 @@ def run_simulation(self): left_monsters_data = {} right_monsters_data = {} - left_monsters_dict, right_monsters_dict = ( - self.input_panel.get_monster_counts() - ) + left_monsters_dict, right_monsters_dict = self.input_panel.get_monster_counts() # 获取左侧怪物信息 for monster_id, entry in left_monsters_dict.items(): @@ -1131,9 +1003,7 @@ def run_simulation(self): except ValueError: logger.error(f"Invalid monster ID: {monster_id}") except Exception as e: - logger.error( - f"Error getting monster name for ID {monster_id}: {e}" - ) + logger.error(f"Error getting monster name for ID {monster_id}: {e}") # 获取右侧怪物信息 for monster_id, entry in right_monsters_dict.items(): @@ -1145,20 +1015,13 @@ def run_simulation(self): if monster_name: right_monsters_data[monster_name] = int(count) else: - logger.error( - f"Monster name not found for ID {monster_id}" - ) + logger.error(f"Monster name not found for ID {monster_id}") except ValueError: logger.error(f"Invalid monster ID: {monster_id}") except Exception as e: - logger.error( - f"Error getting monster name for ID {monster_id}: {e}" - ) + logger.error(f"Error getting monster name for ID {monster_id}: {e}") - simulation_data = { - "left": left_monsters_data, - "right": right_monsters_data, - } + simulation_data = {"left": left_monsters_data, "right": right_monsters_data} json_data = json.dumps(simulation_data, ensure_ascii=False) logger.info(f"Simulation data JSON: {json_data}") @@ -1167,40 +1030,29 @@ def run_simulation(self): # 启动main_sim.py子进程 (非阻塞) # Use sys.executable to ensure the same Python interpreter is used process = subprocess.Popen( - [sys.executable, "main_sim.py"], - stdin=subprocess.PIPE, - text=True, - encoding="utf-8", + [sys.executable, "main_sim.py"], stdin=subprocess.PIPE, text=True, encoding="utf-8" ) # 通过stdin传递JSON数据并关闭stdin process.stdin.write(json_data) process.stdin.close() except FileNotFoundError: - QMessageBox.critical( - self, "错误", "未找到 main_sim.py 文件,请检查路径。" - ) + QMessageBox.critical(self, "错误", "未找到 main_sim.py 文件,请检查路径。") except Exception as e: - QMessageBox.critical( - self, "错误", f"启动模拟器时发生错误: {str(e)}" - ) + QMessageBox.critical(self, "错误", f"启动模拟器时发生错误: {str(e)}") def get_monster_name_by_id(self, monster_id: int): """根据怪物ID获取怪物名称""" - # Need to import MONSTER_MAPPING from src.simulation.utils + # Need to import MONSTER_MAPPING from simulator.utils try: - from src.simulation.utils import MONSTER_MAPPING + from simulator.utils import MONSTER_MAPPING # Adjust for 1-based UI IDs vs 0-based mapping keys monster_name = MONSTER_MAPPING.get(monster_id - 1) if not monster_name: - logger.error( - f"Monster ID {monster_id} not found in MONSTER_MAPPING." - ) + logger.error(f"Monster ID {monster_id} not found in MONSTER_MAPPING.") return monster_name except ImportError: - logger.error( - "Error importing MONSTER_MAPPING from src.simulation.utils" - ) + logger.error("Error importing MONSTER_MAPPING from simulator.utils") return None def update_game_mode(self, mode): @@ -1219,9 +1071,7 @@ def update_stats(self, total, incorrect, duration): def update_image_display(self, qimage): self.image_display.setPixmap( QPixmap.fromImage(qimage).scaled( - self.image_display.width(), - self.image_display.height(), - Qt.AspectRatioMode.KeepAspectRatio, + self.image_display.width(), self.image_display.height(), Qt.AspectRatioMode.KeepAspectRatio ) ) @@ -1231,28 +1081,21 @@ def package_data_and_show(self): if zip_filename: # 在文件浏览器中高亮显示文件 subprocess.run(f'explorer /select,"{zip_filename}"') - QMessageBox.information( - self, "成功", f"数据已打包到 {zip_filename}" - ) + QMessageBox.information(self, "成功", f"数据已打包到 {zip_filename}") else: - QMessageBox.warning( - self, "警告", "没有找到可以打包的数据目录。" - ) + QMessageBox.warning(self, "警告", "没有找到可以打包的数据目录。") except Exception as e: QMessageBox.critical(self, "错误", f"打包数据时发生错误: {str(e)}") + def toggle_always_on_top(self): if self.windowFlags() & Qt.WindowType.WindowStaysOnTopHint: - self.setWindowFlags( - self.windowFlags() & ~Qt.WindowType.WindowStaysOnTopHint - ) + self.setWindowFlags(self.windowFlags() & ~Qt.WindowType.WindowStaysOnTopHint) self.always_on_top_button.setText("窗口置顶") else: - self.setWindowFlags( - self.windowFlags() | Qt.WindowType.WindowStaysOnTopHint - ) + self.setWindowFlags(self.windowFlags() | Qt.WindowType.WindowStaysOnTopHint) self.always_on_top_button.setText("取消置顶") - self.show() # Reapply window flags + self.show() # Reapply window flags def closeEvent(self, event): """窗口关闭时的处理""" diff --git a/main.spec b/main.spec index 924881c..05b2505 100644 --- a/main.spec +++ b/main.spec @@ -11,8 +11,6 @@ a_main = Analysis( pathex=[project_root], binaries=[], datas=[ - ('src', 'src'), - ('vendor', 'vendor'), ('.venv/Lib/site-packages/rapidocr/default_models.yaml', 'rapidocr'), ('.venv/Lib/site-packages/rapidocr/config.yaml', 'rapidocr'), ('.venv/Lib/site-packages/rapidocr/models', 'rapidocr/models') @@ -21,7 +19,7 @@ a_main = Analysis( hookspath=[], hooksconfig={}, runtime_hooks=[], - excludes=['torch', 'torchvision', 'matplotlib', 'sklearn', 'scikit-learn', 'scipy', 'PyQt6.QtPdf', 'PyQt6.QtNetwork', 'predict', 'onnxscript'], + excludes=['train', 'torch', 'torchvision', 'matplotlib', 'sklearn', 'scikit-learn', 'scipy', 'PyQt6.QtPdf', 'PyQt6.QtNetwork', 'predict', 'onnxscript'], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher, @@ -42,7 +40,7 @@ exe_main = EXE( a_main.scripts, [], exclude_binaries=True, - name='CannotMax', + name='main', debug=False, bootloader_ignore_signals=False, strip=False, @@ -53,7 +51,7 @@ exe_main = EXE( target_arch=None, codesign_identity=None, entitlements_file=None, - icon=['src\\resources\\assets\\icons\\icon_64x64.ico'], + icon=['ico\\icon_64x64.ico'], ) # 多开管理器分析 @@ -62,8 +60,6 @@ a_multi = Analysis( pathex=[project_root], binaries=[], datas=[ - ('src', 'src'), - ('vendor', 'vendor'), ('.venv/Lib/site-packages/rapidocr/default_models.yaml', 'rapidocr'), ('.venv/Lib/site-packages/rapidocr/config.yaml', 'rapidocr'), ('.venv/Lib/site-packages/rapidocr/models', 'rapidocr/models') @@ -72,7 +68,7 @@ a_multi = Analysis( hookspath=[], hooksconfig={}, runtime_hooks=[], - excludes=['torch', 'torchvision', 'matplotlib', 'sklearn', 'scikit-learn', 'scipy', 'PyQt6.QtPdf', 'PyQt6.QtNetwork', 'predict', 'onnxscript'], + excludes=['train', 'torch', 'torchvision', 'matplotlib', 'sklearn', 'scikit-learn', 'scipy', 'PyQt6.QtPdf', 'PyQt6.QtNetwork', 'predict', 'onnxscript'], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher, @@ -100,7 +96,7 @@ exe_multi = EXE( target_arch=None, codesign_identity=None, entitlements_file=None, - icon=['src\\resources\\assets\\icons\\icon_64x64.ico'], + icon=['ico\\icon_64x64.ico'], ) coll = COLLECT( diff --git a/main_old.py b/main_old.py new file mode 100644 index 0000000..89d18ed --- /dev/null +++ b/main_old.py @@ -0,0 +1,650 @@ +import logging +import subprocess +import threading +import time +import tkinter as tk +from tkinter import messagebox +import numpy as np +import math +from PIL import Image, ImageTk +from predict import CannotModel +import loadData +import recognize +from train import UnitAwareTransformer +from recognize import MONSTER_COUNT +from similar_history_match import HistoryMatch +from auto_fetch import AutoFetch + +logging.getLogger().setLevel(logging.DEBUG) +logging.getLogger("PIL").setLevel(logging.INFO) +stream_handler = logging.StreamHandler() +formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s",) +stream_handler.setFormatter(formatter) +logging.getLogger().addHandler(stream_handler) +logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) + + +class ArknightsApp: + def __init__(self, root): + self.root = root + self.root.title("Arknights Neural Network") + self.history_match = HistoryMatch() + + self.main_panel = tk.Frame(self.root) + self.main_panel.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) + + # 鼠标滚轮滚动历史面板 + self.root.bind_all("", self._on_mousewheel) + self.root.bind_all("", self._on_shift_mousewheel) + # 运行 + self.no_region = True + self.first_recognize = True + + # 尝试连接模拟器 + self.adb_connector = loadData.AdbConnector() + self.adb_connector.connect() + + # 用户选项 + self.is_invest = tk.BooleanVar(value=False) # 添加投资状态变量 + self.game_mode = tk.StringVar(value="单人") # 添加游戏模式变量,默认单人模式 + self.device_serial = tk.StringVar(value="127.0.0.1:5555") # 添加设备序列号变量 + + # 数据缓存 + self.left_monsters = {} + self.right_monsters = {} + self.images = {} + self.progress_var = tk.StringVar() + + self.load_images() + self.create_widgets() + + # 怪物识别模块 + self.recognizer = recognize.RecognizeMonster() + + # 历史对局面板 + self.history_visible = False + self.history_container = tk.Frame(self.root, bd=1, relief="sunken") + + # Canvas & Scrollbars + self.history_canvas = tk.Canvas(self.history_container, bg="white") + self.history_vscroll = tk.Scrollbar( + self.history_container, orient="vertical", command=self.history_canvas.yview + ) + self.history_hscroll = tk.Scrollbar( + self.history_container, orient="horizontal", command=self.history_canvas.xview + ) + + self.history_canvas.configure( + yscrollcommand=self.history_vscroll.set, xscrollcommand=self.history_hscroll.set + ) + + # 真正放内容的 Frame + self.history_frame = tk.Frame(self.history_canvas) + self.history_canvas.create_window((0, 0), window=self.history_frame, anchor="nw") + + # 更新 scroll region + self.history_frame.bind( + "", + lambda e: self.history_canvas.configure(scrollregion=self.history_canvas.bbox("all")), + ) + + # Canvas + 两条滚动条在 history_container 里排版 + self.history_canvas.grid(row=0, column=0, sticky="nsew") + self.history_vscroll.grid(row=0, column=1, sticky="ns") + self.history_hscroll.grid(row=1, column=0, sticky="ew") + + # 让 Canvas 单元格可伸缩 + self.history_container.grid_rowconfigure(0, weight=1) + self.history_container.grid_columnconfigure(0, weight=1) + + # 模型相关属性 + self.cannot_model = CannotModel() + + + def _on_mousewheel(self, event): + """滑动鼠标滚轮 → 垂直滚动错题本面板""" + self.history_canvas.yview_scroll(int(-1 * (event.delta / 120)), "units") + + def _on_shift_mousewheel(self, event): + """按住 Shift + 滚轮 → 水平滚动错题本面板""" + self.history_canvas.xview_scroll(int(-1 * (event.delta / 120)), "units") + + def load_images(self): + # 获取系统缩放因子 + scaling_factor = self.root.tk.call("tk", "scaling") + base_size = 30 + icon_size = int(base_size * scaling_factor) # 动态计算图标大小 + + for i in range(1, MONSTER_COUNT + 1): + # 使用PIL打开图像并缩放 + img = Image.open(f"images/{i}.png") + width, height = img.size + + # 计算缩放比例,保持宽高比且不超过目标尺寸 + ratio = min(icon_size / width, icon_size / height) + new_size = (int(width * ratio), int(height * ratio)) + + # 高质量缩放 + img_resized = img.resize(new_size, Image.Resampling.LANCZOS) + + # 转换为Tkinter兼容格式 + photo_img = ImageTk.PhotoImage(img_resized) + self.images[str(i)] = photo_img + + def create_widgets(self): + # 创建顶层容器 + self.top_container = tk.Frame(self.main_panel) + self.bottom_container = tk.Frame(self.main_panel) + + # 顶部容器布局(填充整个水平空间) + self.top_container.pack(side=tk.TOP, fill=tk.X, pady=10) + self.bottom_container.pack(side=tk.TOP, fill=tk.BOTH, expand=True, pady=10) + + # 创建居中容器用于放置左右怪物框 + self.monster_center = tk.Frame(self.top_container) + self.monster_center.pack(side=tk.TOP, anchor="center") + + # 创建左右怪物容器(添加边框和背景色) + self.left_frame = tk.Frame( + self.monster_center, borderwidth=2, relief="groove", padx=5, pady=5 + ) + self.right_frame = tk.Frame( + self.monster_center, borderwidth=2, relief="groove", padx=5, pady=5 + ) + + # 添加左右标题 + tk.Label(self.left_frame, text="左侧怪物", font=("Helvetica", 10, "bold")).grid( + row=0, columnspan=10 + ) + tk.Label(self.right_frame, text="右侧怪物", font=("Helvetica", 10, "bold")).grid( + row=0, columnspan=10 + ) + + # 左右布局(添加显式间距并居中) + self.left_frame.pack(side=tk.LEFT, padx=10, anchor="n", pady=5) + self.right_frame.pack(side=tk.RIGHT, padx=10, anchor="n", pady=5) + + for side, frame, monsters in [ + ("left", self.left_frame, self.left_monsters), + ("right", self.right_frame, self.right_monsters), + ]: + row_n = 6 + monsters_per_row = math.ceil(MONSTER_COUNT / row_n) + for row in range(row_n): + start = row * monsters_per_row + 1 + end = min((row + 1) * monsters_per_row + 1, MONSTER_COUNT + 1) + for i in range(start, end): + # 图片标签(缩小尺寸) + tk.Label(frame, image=self.images[str(i)], padx=1, pady=1).grid( + row=row * 2, column=i - start, sticky="ew" + ) + # 输入框(保持宽度5) + monsters[str(i)] = tk.Entry(frame, width=5) + monsters[str(i)].grid( + row=row * 2 + 1, column=i - start, pady=(0, 1) # 减小底部间距 + ) + + # 调整列权重使布局更紧凑 + for col in range(monsters_per_row): + frame.grid_columnconfigure(col, weight=1, minsize=25) # 适当调整最小列宽 + + # 结果显示区域(增加边框) + self.result_frame = tk.Frame(self.bottom_container, relief="ridge", borderwidth=1) + self.result_frame.pack(fill=tk.X, pady=5) + + # 使用更醒目的字体 + self.result_label = tk.Label( + self.result_frame, text="Prediction: ", font=("Helvetica", 16, "bold"), fg="blue" + ) + self.result_label.pack(pady=3) + self.stats_label = tk.Label(self.result_frame, text="", font=("Helvetica", 12), fg="green") + self.stats_label.pack(pady=3) + + # 按钮区域容器(增加边框和背景) + self.button_frame = tk.Frame( + self.bottom_container, relief="groove", borderwidth=2, padx=10, pady=10 + ) + self.button_frame.pack(fill=tk.BOTH, expand=True) + + # 按钮布局(分左右两列布局) + left_buttons = tk.Frame(self.button_frame) + center_buttons = tk.Frame(self.button_frame) # 新增中间按钮容器 + right_buttons = tk.Frame(self.button_frame) + + # 使用grid布局实现均匀分布 + left_buttons.grid(row=0, column=0, sticky="ew") + center_buttons.grid(row=0, column=1, sticky="ew") # 中间列 + right_buttons.grid(row=0, column=2, sticky="ew") + self.button_frame.grid_columnconfigure((0, 1, 2), weight=1) # 均匀分布三列 + + # 左侧按钮列(控制选项) + control_col = tk.Frame(left_buttons) + control_col.pack(anchor="center", expand=True) + + # 时长输入组 + duration_frame = tk.Frame(control_col) + duration_frame.pack(pady=2) + tk.Label(duration_frame, text="训练时长:").pack(side=tk.LEFT) + self.duration_entry = tk.Entry(duration_frame, width=6) + self.duration_entry.insert(0, "-1") + self.duration_entry.pack(side=tk.LEFT, padx=5) + + # 模式选择组 + mode_frame = tk.Frame(control_col) + mode_frame.pack(pady=2) + self.mode_menu = tk.OptionMenu(mode_frame, self.game_mode, "单人", "30人") + self.mode_menu.pack(side=tk.LEFT) + self.invest_checkbox = tk.Checkbutton(mode_frame, text="投资", variable=self.is_invest) + self.invest_checkbox.pack(side=tk.LEFT, padx=5) + + # 中间按钮列(核心操作) + action_col = tk.Frame(center_buttons) + action_col.pack(anchor="center", expand=True) + + # 核心操作按钮 + action_buttons = [("自动获取数据", self.toggle_auto_fetch)] + # 单独处理自动获取数据按钮 + for text, cmd in action_buttons: + btn = tk.Button(action_col, text=text, command=cmd, width=14) # 加宽按钮 + btn.pack(pady=5, ipadx=5) + if text == "自动获取数据": + self.auto_fetch_button = btn + btn.pack(pady=5, ipadx=5) + + # 右侧按钮列(功能按钮) + func_col = tk.Frame(right_buttons) + func_col.pack(anchor="center", expand=True) + + # 预测功能组 + predict_frame = tk.Frame(func_col) + predict_frame.pack(pady=2) + self.predict_button = tk.Button( + predict_frame, text="预测", command=self.predict, width=8, bg="#FFE4B5" + ) + self.predict_button.pack(side=tk.LEFT, padx=2) + + self.recognize_button = tk.Button( + predict_frame, text="识别并预测", command=self.recognize_and_predict, width=10, bg="#98FB98" + ) + self.recognize_button.pack(side=tk.LEFT, padx=2) + + self.reset_button = tk.Button( + predict_frame, text="归零", command=self.reset_entries, width=6 + ) + self.reset_button.pack(side=tk.LEFT, padx=2) + + # 设备序列号组(独立行) + serial_frame = tk.Frame(func_col) + serial_frame.pack(pady=5) + + self.reselect_button = tk.Button( + serial_frame, text="选择范围", command=self.reselect_roi, width=10 + ) + self.reselect_button.pack(side=tk.LEFT) + + tk.Label(serial_frame, text="设备号:").pack(side=tk.LEFT) + self.serial_entry = tk.Entry(serial_frame, textvariable=self.device_serial, width=15) + self.serial_entry.pack(side=tk.LEFT, padx=3) + + self.serial_button = tk.Button( + serial_frame, text="更新", command=self.update_device_serial, width=6 + ) + self.serial_button.pack(side=tk.LEFT) + + # 错题本开关 + self.history_button = tk.Button( + func_col, text="显示错题本", command=self.toggle_history_panel, width=10 + ) + self.history_button.pack(pady=4) # 可以 side=tk.TOP / BOTTOM 都行 + + def toggle_history_panel(self): + if not self.history_visible: + self.history_container.pack(side="right", fill="both", padx=5, pady=5) + self.history_button.config(text="隐藏错题本") + for w in self.history_frame.winfo_children(): + w.destroy() + self.render_history(self.history_frame) + self.history_canvas.configure(scrollregion=self.history_canvas.bbox("all")) + else: + self.history_container.pack_forget() + self.history_button.config(text="显示错题本") + self.history_visible = not self.history_visible + + def render_history(self, parent): + # 准备输入数据(完全匹配ArknightsDataset的处理方式) + left_counts = np.zeros(MONSTER_COUNT, dtype=np.int16) + right_counts = np.zeros(MONSTER_COUNT, dtype=np.int16) + # 从界面获取数据(空值处理为0) + for name, entry in self.left_monsters.items(): + value = entry.get() + left_counts[int(name) - 1] = int(value) if value.isdigit() else 0 + for name, entry in self.right_monsters.items(): + value = entry.get() + right_counts[int(name) - 1] = int(value) if value.isdigit() else 0 + + self.history_match.render_similar_matches(left_counts, right_counts) + try: + left_rate = self.history_match.left_rate + right_rate = self.history_match.right_rate + cur_left = self.history_match.cur_left + cur_right = self.history_match.cur_right + sims = self.history_match.sims + swap = self.history_match.swap + top20_idx = self.history_match.top20_idx + # 清空旧内容 + for w in parent.winfo_children(): + w.destroy() + + # 标题 + head = tk.Frame(parent) + head.pack(fill="x", pady=4) + fgL, fgR = ("#E23F25", "#666") if left_rate > right_rate else ("#666", "#25ace2") + tk.Label(head, text="近5条左右胜率:", font=("Helvetica", 12, "bold")).pack(side="left") + tk.Label( + head, text=f"左边 {left_rate:.2%} ", fg=fgL, font=("Helvetica", 12, "bold") + ).pack(side="left") + tk.Label( + head, text=f"右边 {right_rate:.2%}", fg=fgR, font=("Helvetica", 12, "bold") + ).pack(side="left") + + # 错题本主体渲染 + self._history_parent = parent + self._top20 = top20_idx.tolist() + self._sims = sims + self._swap = swap + self._batch_idx = 0 + + # 调整Canvas宽度 + self.history_canvas.config(width=700) # 增加Canvas宽度 + self.history_frame.config(width=700) # 增加Frame宽度 + + parent.after(0, lambda: self._render_batch(batch_size=5)) + + except Exception as e: + print("[渲染错题本失败]", e) + + def _render_batch(self, batch_size=5): + start = self._batch_idx * batch_size + end = start + batch_size + history_match = self.history_match + parent = self._history_parent + top20 = self._top20 + sims = self._sims + swap = self._swap + + for rank, idx in enumerate(top20[start:end], start + 1): + sims_val = sims[idx] + swapped = swap[idx] + Lh, Rh = (history_match.past_left if not swapped else history_match.past_right)[idx], ( + history_match.past_right if not swapped else history_match.past_left + )[idx] + lab = history_match.labels[idx] + if swapped: + lab = "L" if lab == "R" else "R" + winL, winR = (lab == "L"), (lab == "R") + + # csv中的行数=局数 + real_no = idx + 2 + + row = tk.Frame(parent, pady=6) + row.pack(fill="x") + + # 左侧信息区域 + info_frame = tk.Frame(row) + info_frame.pack(side="left", fill="y", padx=5) + + # 局数 + tk.Label( + info_frame, + text=f"第 {real_no} 局", + font=("Helvetica", 10), + ).pack(anchor="w") + + # 相似度 + tk.Label( + info_frame, text=f"{rank}. 相似度 {sims_val:.2f}", font=("Helvetica", 10, "bold") + ).pack(anchor="w") + + # 右侧阵容区域 + roster_frame = tk.Frame(row) + roster_frame.pack(side="right", fill="both", expand=True) + + # 左右阵容渲染(修改为水平排列) + for side_name, is_left, win, bg_color, fg_color in [ + ("左", True, winL, "#ffe5e5", "#E23F25"), + ("右", False, winR, "#e5e5ff", "#25ace2"), + ]: + pane = tk.Frame( + roster_frame, + bd=2, + relief="solid", + bg=bg_color, + highlightbackground="red" if winL and is_left or winR and not is_left else "#aaa", + highlightthickness=2, + ) + pane.pack(side="left" if is_left else "right", fill="both", expand=True, padx=5) + + # 侧边阵容 + tk.Label( + pane, + text=f"{side_name}边", + fg=fg_color if win else "#666", + bg=bg_color if win else "#f0f0f0", + font=("Helvetica", 9, "bold"), + ).pack(anchor="w", padx=4) + inner = tk.Frame(pane, bg=bg_color if win else "#f0f0f0") + inner.pack(fill="x", padx=4, pady=2) + data = Lh if is_left else Rh + for i, count in enumerate(data): + if count > 0: + img = self.images[str(i + 1)] + tk.Label(inner, image=img, bg=bg_color if win else "#f0f0f0").pack( + side="left", padx=2 + ) + tk.Label( + inner, text=f"×{int(count)}", bg=bg_color if win else "#f0f0f0" + ).pack(side="left", padx=(0, 6)) + self._batch_idx += 1 + # 更新滚动区域 + self.history_canvas.configure(scrollregion=self.history_canvas.bbox("all")) + if end < len(top20): + parent.after(50, lambda: self._render_batch(batch_size)) + + def reset_entries(self): + for entry in self.left_monsters.values(): + entry.delete(0, tk.END) + entry.config(bg="white") # Reset color + for entry in self.right_monsters.values(): + entry.delete(0, tk.END) + entry.config(bg="white") # Reset color + self.result_label.config(text="Prediction: ") + + def get_prediction(self): + # 准备输入数据(完全匹配ArknightsDataset的处理方式) + left_counts = np.zeros(MONSTER_COUNT, dtype=np.int16) + right_counts = np.zeros(MONSTER_COUNT, dtype=np.int16) + # 从界面获取数据(空值处理为0) + for name, entry in self.left_monsters.items(): + value = entry.get() + left_counts[int(name) - 1] = int(value) if value.isdigit() else 0 + for name, entry in self.right_monsters.items(): + value = entry.get() + right_counts[int(name) - 1] = int(value) if value.isdigit() else 0 + + try: + prediction = self.cannot_model.get_prediction(left_counts, right_counts) + return prediction + except FileNotFoundError: + messagebox.showerror("错误", "未找到模型文件,请先点击「训练」按钮") + return 0.5 + except RuntimeError as e: + if "size mismatch" in str(e): + messagebox.showerror("错误", "模型结构不匹配!请删除旧模型并重新训练") + else: + messagebox.showerror("错误", f"模型加载失败: {str(e)}") + return 0.5 + except ValueError: + messagebox.showerror("错误", "请输入有效的数字(0或正整数)") + return 0.5 + except Exception as e: + messagebox.showerror("错误", f"预测时发生错误: {str(e)}") + return 0.5 + + def update_prediction(self, prediction): + # 结果解释(注意:prediction直接对应标签'R'的概率) + right_win_prob = prediction # 模型输出的是右方胜率 + left_win_prob = 1 - right_win_prob + + # 格式化输出 + result_text = ( + f"预测结果:\n\n左方胜率: {left_win_prob:.2%}\t\t" f"右方胜率: {right_win_prob:.2%}" + ) + + # 根据胜率设置颜色(保持与之前一致) + self.result_label.config(text=result_text) + if left_win_prob > 0.7: + self.result_label.config(fg="#E23F25", font=("Helvetica", 12, "bold")) # red + elif left_win_prob > 0.6: + self.result_label.config(fg="#E23F25", font=("Helvetica", 12, "bold")) + elif right_win_prob > 0.7: + self.result_label.config(fg="#25ace2", font=("Helvetica", 12, "bold")) # blue + elif right_win_prob > 0.6: + self.result_label.config(fg="#25ace2", font=("Helvetica", 12, "bold")) + else: + self.result_label.config(fg="black", font=("Helvetica", 12, "bold")) + + def predict(self): + # 保存当前预测结果用于后续数据收集 + self.current_prediction = self.get_prediction() + self.update_prediction(self.current_prediction) + + if self.history_visible: + for w in self.history_frame.winfo_children(): + w.destroy() + self.render_history(self.history_frame) + self.history_canvas.configure(scrollregion=self.history_canvas.bbox("all")) + + def recognize(self): + # 如果正在进行自动获取数据,从adb加载截图 + if hasattr(self, "auto_fetch") and self.auto_fetch.auto_fetch_running: + screenshot = self.adb_connector.capture_screenshot() + else: + screenshot = None + + if self.no_region: # 如果尚未选择区域,从adb获取截图 + if self.first_recognize: # 首次识别时,尝试连接adb + self.adb_connector.connect() + self.first_recognize = False + screenshot = self.adb_connector.capture_screenshot() + + results = self.recognizer.process_regions(screenshot) + self.reset_entries() + return results, screenshot + + def update_monster(self, results): + # 处理结果 + for res in results: + region_id = res["region_id"] + if "error" not in res: + matched_id = res["matched_id"] + number = res["number"] + if matched_id != 0: + if region_id < 3: + entry = self.left_monsters[str(matched_id)] + else: + entry = self.right_monsters[str(matched_id)] + entry.delete(0, tk.END) + entry.insert(0, number) + # Highlight the image if the entry already has data + if entry.get(): + entry.config(bg="yellow") + else: + if "matched_id" in res: + matched_id = res["matched_id"] + if region_id < 3: + entry = self.left_monsters[str(matched_id)] + else: + entry = self.right_monsters[str(matched_id)] + entry.delete(0, tk.END) + entry.config(bg="red") + entry.insert(0, "Error") + + def recognize_and_predict(self): + results, screenshot = self.recognize() + self.update_monster(results) + prediction = self.get_prediction() + self.update_prediction(prediction) + + # 历史对局 + if self.history_visible: + for w in self.history_frame.winfo_children(): + w.destroy() + self.render_history(self.history_frame) + self.history_canvas.configure(scrollregion=self.history_canvas.bbox("all")) + return prediction, results, screenshot + + def reselect_roi(self): + self.recognizer.select_roi() + self.no_region = False + + def start_training(self): + threading.Thread(target=self.train_model).start() + + def train_model(self): + # Update progress + self.root.update_idletasks() + + # Simulate training process + subprocess.run(["python", "train.py"]) + self.root.update_idletasks() + + messagebox.showinfo("Info", "Model trained successfully") + + def start_callback(self): + self.auto_fetch_button.config(text="停止自动获取数据") + pass + + def stop_callback(self): + self.auto_fetch_button.config(text="自动获取数据") + pass + + def update_statistics(self): + elapsed_time = time.time() - self.auto_fetch.start_time if self.auto_fetch.start_time else 0 + hours, remainder = divmod(elapsed_time, 3600) + minutes, _ = divmod(remainder, 60) + stats_text = ( + f"总共填写次数: {self.auto_fetch.total_fill_count} , " + f"填写×次数: {self.auto_fetch.incorrect_fill_count}, " + f"当次运行时长: {int(hours)}小时{int(minutes)}分钟" + ) + self.stats_label.config(text=stats_text) + + def toggle_auto_fetch(self): + if not (hasattr(self, "auto_fetch") and self.auto_fetch.auto_fetch_running): + self.auto_fetch = AutoFetch( + self.adb_connector, + self.game_mode, + self.is_invest, + update_monster_callback=self.update_monster, + update_prediction_callback=self.update_prediction, + updater=self.update_statistics, + start_callback=self.start_callback, + stop_callback=self.stop_callback, + training_duration=float(self.duration_entry.get()) * 3600, # 获取训练时长 + ) + self.auto_fetch.start_auto_fetch() + else: + self.auto_fetch.stop_auto_fetch() + + def update_device_serial(self): + """更新设备序列号""" + new_serial = self.device_serial.get() + self.adb_connector.update_device_serial(new_serial) # 重新初始化设备连接 + messagebox.showinfo("提示", f"已更新模拟器序列号为: {new_serial}") + + +if __name__ == "__main__": + root = tk.Tk() + app = ArknightsApp(root) + root.mainloop() diff --git a/main_sim.py b/main_sim.py index 8395dde..9ff686b 100644 --- a/main_sim.py +++ b/main_sim.py @@ -3,21 +3,22 @@ import tkinter as tk from tkinter import messagebox from PIL import Image, ImageTk -from src.utils.unit import Unit -import json -import sys +import math +from unit import Unit # 确保 Unit 已导入 +import json # REMOVED_TEAM_INTERFACE: Added missing import for the main block +import random # REMOVED_TEAM_INTERFACE: Added missing import for the main block +import sys # Import sys for stdin import logging -from src.simulation.battle_field import Battlefield -from src.simulation.utils import Faction -from src.simulation.vector2d import FastVector -from src.simulation.monsters import AttackState, Monster -from src.core.config import MONSTER_DATA, MONSTER_IMAGES -from src.recognition.recognize import MONSTER_COUNT +from simulator.battle_field import Battlefield # 确保 Battlefield 已导入 +from simulator.monsters import MonsterFactory # 确保 MonsterFactory 已导入 +from simulator.utils import MONSTER_MAPPING, REVERSE_MONSTER_MAPPING, Faction # 根据你的实际路径调整 +from simulator.vector2d import FastVector # 确保 FastVector 已导入 +from simulator.monsters import AttackState, Monster, MonsterFactory +from recognize import MONSTER_COUNT logger = logging.getLogger(__name__) - class AppState(Enum): INITIAL = auto() # 初始状态 SETUP = auto() # 部署阶段 @@ -34,81 +35,63 @@ def __init__(self, ui_update_callback): def transition_to(self, new_state): """状态转换并触发UI更新""" allowed_transitions = { - AppState.INITIAL: [ - AppState.INITIAL, - AppState.SETUP, - AppState.ENDED, - ], - AppState.SETUP: [ - AppState.INITIAL, - AppState.SETUP, - AppState.SIMULATING, - ], + AppState.INITIAL: [AppState.INITIAL, AppState.SETUP, AppState.ENDED], + AppState.SETUP: [AppState.INITIAL, AppState.SETUP, AppState.SIMULATING], AppState.SIMULATING: [AppState.PAUSED, AppState.ENDED], AppState.PAUSED: [AppState.SIMULATING, AppState.SETUP], - AppState.ENDED: [AppState.INITIAL, AppState.SETUP], + AppState.ENDED: [AppState.INITIAL, AppState.SETUP] } if new_state in allowed_transitions[self.state]: self.state = new_state self.ui_update() else: - logger.error( - f"Illegal state transition from {self.state} to {new_state}" - ) + logger.error(f"Illegal state transition from {self.state} to {new_state}") def get_control_states(self): """返回各控件的状态字典""" states = { - "deploy": {"state": tk.NORMAL, "text": "部署怪物"}, - "confirm_start": {"state": tk.DISABLED}, - "pause": {"state": tk.DISABLED, "text": "暂停"}, - "restore": {"state": tk.DISABLED}, - "speed_entry": {"state": tk.NORMAL}, - "clear": {"state": tk.NORMAL}, - "timer": {"text": ""}, + 'deploy': {'state': tk.NORMAL, 'text': '部署怪物'}, + 'confirm_start': {'state': tk.DISABLED}, + 'pause': {'state': tk.DISABLED, 'text': '暂停'}, + 'restore': {'state': tk.DISABLED}, + 'speed_entry': {'state': tk.NORMAL}, + 'clear': {'state': tk.NORMAL}, + 'timer': {'text': ''} } if self.state == AppState.INITIAL: pass elif self.state == AppState.SETUP: - states.update( - { - "deploy": {"state": tk.NORMAL, "text": "重新部署"}, - "confirm_start": {"state": tk.NORMAL}, - "timer": {"text": "未开始"}, - } - ) + states.update({ + 'deploy': {'state': tk.NORMAL, 'text': '重新部署'}, + 'confirm_start': {'state': tk.NORMAL}, + 'timer': {'text': '未开始'} + }) elif self.state == AppState.SIMULATING: - states.update( - { - "deploy": {"state": tk.DISABLED, "text": "重新部署"}, - "confirm_start": {"state": tk.DISABLED}, - "pause": {"state": tk.NORMAL, "text": "暂停"}, - "clear": {"state": tk.DISABLED}, - } - ) + states.update({ + 'deploy': {'state': tk.DISABLED, 'text': '重新部署'}, + 'confirm_start': {'state': tk.DISABLED}, + 'pause': {'state': tk.NORMAL, 'text': '暂停'}, + 'clear': {'state': tk.DISABLED}, + }) elif self.state == AppState.PAUSED: - states.update( - { - "restore": {"state": tk.NORMAL}, - "pause": {"state": tk.NORMAL, "text": "继续"}, - "deploy": {"state": tk.NORMAL, "text": "重新部署"}, - } - ) + states.update({ + 'restore': {'state': tk.NORMAL}, + 'pause': {'state': tk.NORMAL, 'text': '继续'}, + 'deploy': {'state': tk.NORMAL, 'text': '重新部署'} + }) elif self.state == AppState.ENDED: - states.update( - { - "deploy": {"state": tk.DISABLED, "text": "重新部署"}, - "restore": {"state": tk.NORMAL}, - "pause": {"state": tk.DISABLED, "text": "暂停"}, - "timer": {"text": "战斗结束"}, - } - ) + states.update({ + 'deploy': {'state': tk.DISABLED, 'text': '重新部署'}, + 'restore': {'state': tk.NORMAL}, + 'pause': {'state': tk.DISABLED, 'text': '暂停'}, + 'timer': {'text': '战斗结束'} + }) return states @@ -142,9 +125,7 @@ def __init__(self, master: tk.Tk, battle_data): self.initial_battlefield = None # 新增状态 self.setup_phase_active = False # 怪物部署和调整阶段是否激活 - self.selected_monster_for_drag = ( - None # 当前拖动的怪物对象 (Monster 类型) - ) + self.selected_monster_for_drag = None # 当前拖动的怪物对象 (Monster 类型) self.message_label = None # 用于显示提示信息的标签 self.message_timer_id = None # 用于定时清除提示信息的ID @@ -155,35 +136,25 @@ def __init__(self, master: tk.Tk, battle_data): self.state_machine.transition_to(AppState.INITIAL) self.enter_setup_phase() - def _resolve_monster_icon_key(self, monster_name: str) -> str: - """根据怪物名称解析对应的图标文件名。""" - if monster_name in self.monster_icon_key_map: - return self.monster_icon_key_map[monster_name] - - if monster_name in MONSTER_IMAGES: - return monster_name - - return self.default_icon_key - def update_ui_state(self): """根据当前状态更新所有控件状态""" states = self.state_machine.get_control_states() self.deploy_button.config( - state=states["deploy"]["state"], text=states["deploy"]["text"] - ) - self.confirm_start_button.config( - state=states["confirm_start"]["state"] + state=states['deploy']['state'], + text=states['deploy']['text'] ) + self.confirm_start_button.config(state=states['confirm_start']['state']) self.pause_button.config( - state=states["pause"]["state"], text=states["pause"]["text"] + state=states['pause']['state'], + text=states['pause']['text'] ) - self.restore_button.config(state=states["restore"]["state"]) - self.speed_entry.config(state=states["speed_entry"]["state"]) - self.clear_button.config(state=states["clear"]["state"]) + self.restore_button.config(state=states['restore']['state']) + self.speed_entry.config(state=states['speed_entry']['state']) + self.clear_button.config(state=states['clear']['state']) - if states["timer"]["text"] != "": - self.timer_label.config(text=states["timer"]["text"]) + if states['timer']['text'] != '': + self.timer_label.config(text=states['timer']['text']) def hide_window(self): if self.state_machine.state == AppState.SIMULATING: @@ -193,10 +164,8 @@ def hide_window(self): def load_assets(self): self.icons = {} - self.monster_icon_key_map = {} - self.default_icon_key = "empty" if "empty" in MONSTER_IMAGES else "" try: - with open("src/simulation/monsters.json", encoding="utf-8") as f: + with open("simulator/monsters.json", encoding='utf-8') as f: self.monster_data = json.load(f)["monsters"] except FileNotFoundError: logger.error("错误: monsters.json 未找到,请检查路径!") @@ -204,47 +173,21 @@ def load_assets(self): return - # 构建“名称/原始名称 -> 图标文件名”的映射,优先使用原始名称对应的图片。 - for _, row in MONSTER_DATA.iterrows(): - monster_name = str(row["名称"]) - original_name = str(row["原始名称"]) - - icon_key = None - if original_name in MONSTER_IMAGES: - icon_key = original_name - elif monster_name in MONSTER_IMAGES: - icon_key = monster_name - - if icon_key is None: - icon_key = self.default_icon_key - - self.monster_icon_key_map[monster_name] = icon_key - self.monster_icon_key_map[original_name] = icon_key - - # 为实际存在的图片创建 Tk 图像,未命中的统一回退到灰色或 empty。 - for image_key, image_array in MONSTER_IMAGES.items(): + for i in range(self.num_monsters): + image_file_id = i + 1 try: - if image_array is None: - raise ValueError("图像数据为空") - - image = Image.fromarray(image_array[:, :, ::-1]) - image_40 = image.resize((40, 40)) - self.icons[image_key] = { - "red": ImageTk.PhotoImage(image_40, master=self.master), - "blue": ImageTk.PhotoImage( - image_40.transpose(Image.FLIP_LEFT_RIGHT), - master=self.master, - ), + image = Image.open(f'images/{image_file_id}.png') + self.icons[i] = { + "red": ImageTk.PhotoImage(image.resize((40, 40))), + "blue": ImageTk.PhotoImage(image.resize((40, 40)).transpose(Image.FLIP_LEFT_RIGHT)) } except Exception as e: - logger.error(f"加载图标错误 (图标键: {image_key}): {str(e)}") - - if self.default_icon_key and self.default_icon_key not in self.icons: - fallback = Image.new("RGB", (40, 40), "gray") - self.icons[self.default_icon_key] = { - "red": ImageTk.PhotoImage(fallback, master=self.master), - "blue": ImageTk.PhotoImage(fallback, master=self.master), - } + # 同样,show_message_below_button 可能还不可用 + logger.error(f"加载图标错误 (图标键: {i}, 文件名ID: {image_file_id}): {str(e)}") + self.icons[i] = { + "red": ImageTk.PhotoImage(Image.new("RGB", (40, 40), "gray")), + "blue": ImageTk.PhotoImage(Image.new("RGB", (40, 40), "gray")) + } def init_battlefield_for_setup(self): self.state_machine.transition_to(AppState.SETUP) @@ -254,9 +197,7 @@ def init_battlefield_for_setup(self): left_army_config = self.battle_data.get("left", {}) right_army_config = self.battle_data.get("right", {}) - self.battle_field.setup_battle( - left_army_config, right_army_config, self.monster_data - ) + self.battle_field.setup_battle(left_army_config, right_army_config, self.monster_data) while self.battle_field.gameTime < 6.0: result = self.battle_field.run_one_frame() if result: @@ -264,10 +205,7 @@ def init_battlefield_for_setup(self): self.refresh_canvas_display() def on_mouse_drag(self, event): - if self.selected_monster_for_drag and self.state_machine.state in [ - AppState.PAUSED, - AppState.SETUP, - ]: + if self.selected_monster_for_drag and self.state_machine.state in [AppState.PAUSED, AppState.SETUP]: new_grid_x = event.x / self.cell_size new_grid_y = event.y / self.cell_size new_grid_x = max(0.25, min(new_grid_x, self.grid_width - 0.25)) @@ -275,9 +213,7 @@ def on_mouse_drag(self, event): self.selected_monster_for_drag.position.x = new_grid_x self.selected_monster_for_drag.position.y = new_grid_y - self.selected_monster_for_drag.target_deployment_position = ( - FastVector(new_grid_x, new_grid_y) - ) + self.selected_monster_for_drag.target_deployment_position = FastVector(new_grid_x, new_grid_y) self.refresh_canvas_display() @@ -294,44 +230,25 @@ def create_widgets(self): self.speed_entry = tk.Entry(speed_frame, width=5) self.speed_entry.pack(side=tk.LEFT) self.speed_entry.insert(0, f"{int(self.speed_multiplier)}") - tk.Button(speed_frame, text="应用", command=self.apply_speed).pack( - side=tk.LEFT - ) + tk.Button(speed_frame, text="应用", command=self.apply_speed).pack(side=tk.LEFT) - self.deploy_button = tk.Button( - top_control_frame, text="部署怪物", command=self.enter_setup_phase - ) + self.deploy_button = tk.Button(top_control_frame, text="部署怪物", command=self.enter_setup_phase) self.deploy_button.pack(side=tk.LEFT, padx=5) - self.confirm_start_button = tk.Button( - top_control_frame, - text="开始战斗", - command=self.start_actual_simulation, - state=tk.DISABLED, - ) + self.confirm_start_button = tk.Button(top_control_frame, text="开始战斗", command=self.start_actual_simulation, + state=tk.DISABLED) self.confirm_start_button.pack(side=tk.LEFT, padx=5) - self.clear_button = tk.Button( - top_control_frame, text="清空战场", command=self.clear_sandbox - ) + self.clear_button = tk.Button(top_control_frame, text="清空战场", command=self.clear_sandbox) self.clear_button.pack(side=tk.LEFT, padx=5) self.timer_label = tk.Label(top_control_frame, text="0.00秒") self.timer_label.pack(side=tk.LEFT, padx=100) - self.pause_button = tk.Button( - top_control_frame, - text="暂停", - command=self.toggle_pause, - state=tk.DISABLED, - ) + self.pause_button = tk.Button(top_control_frame, text="暂停", command=self.toggle_pause, state=tk.DISABLED) self.pause_button.pack(side=tk.LEFT, padx=5) - self.restore_button = tk.Button( - top_control_frame, - text="恢复站位", - command=self.restore_initial_positions, - state=tk.DISABLED, - ) + self.restore_button = tk.Button(top_control_frame, text="恢复站位", command=self.restore_initial_positions, + state=tk.DISABLED) self.restore_button.pack(side=tk.LEFT, padx=5) self.game_over_label = tk.Label(top_control_frame, text="", fg="red") @@ -341,12 +258,7 @@ def create_widgets(self): self.message_label = tk.Label(control_frame, text="", fg="black") self.message_label.pack(pady=2) - self.canvas = tk.Canvas( - self.master, - width=self.canvas_width, - height=self.canvas_height, - bg="white", - ) + self.canvas = tk.Canvas(self.master, width=self.canvas_width, height=self.canvas_height, bg='white') self.canvas.pack(pady=10) self.draw_grid() @@ -354,19 +266,13 @@ def create_widgets(self): self.canvas.bind("", self.on_mouse_drag) self.canvas.bind("", self.on_mouse_up) - def show_message_below_button( - self, message, is_error=False, duration=5000 - ): + def show_message_below_button(self, message, is_error=False, duration=5000): """在按钮下方显示文字提示,并在指定时间后消失""" if self.message_label: - self.message_label.config( - text=message, fg="red" if is_error else "black" - ) + self.message_label.config(text=message, fg="red" if is_error else "black") if self.message_timer_id: self.master.after_cancel(self.message_timer_id) - self.message_timer_id = self.master.after( - duration, self.clear_message_below_button - ) + self.message_timer_id = self.master.after(duration, self.clear_message_below_button) def clear_message_below_button(self): """清除提示信息""" @@ -395,82 +301,37 @@ def apply_speed(self): def draw_grid(self): danger_zone = 0 if self.battle_field and self.battle_field.danger_zone_size() > 0: - danger_zone = min( - self.battle_field.danger_zone_size(), self.grid_height / 2 + 1 - ) - self.canvas.create_rectangle( - 0, - 0, - self.canvas_width, - danger_zone * self.cell_size, - fill="#cccc00", - outline="", - ) - self.canvas.create_rectangle( - 0, - 0, - (danger_zone + 1) * self.cell_size, - self.canvas_height, - fill="#cccc00", - outline="", - ) - self.canvas.create_rectangle( - self.canvas_width, - 0, - self.canvas_width - (danger_zone + 1) * self.cell_size, - self.canvas_height, - fill="#cccc00", - outline="", - ) - self.canvas.create_rectangle( - self.canvas_width, - self.canvas_height, - 0, - self.canvas_height - danger_zone * self.cell_size, - fill="#cccc00", - outline="", - ) + danger_zone = min(self.battle_field.danger_zone_size(), self.grid_height / 2 + 1) + self.canvas.create_rectangle(0, 0, self.canvas_width, danger_zone * self.cell_size, fill='#cccc00', + outline="") + self.canvas.create_rectangle(0, 0, (danger_zone+1) * self.cell_size , self.canvas_height, fill='#cccc00', + outline="") + self.canvas.create_rectangle(self.canvas_width, 0, self.canvas_width - (danger_zone+1) * self.cell_size, + self.canvas_height, fill='#cccc00', outline="") + self.canvas.create_rectangle(self.canvas_width, self.canvas_height, 0, + self.canvas_height - danger_zone * self.cell_size, fill='#cccc00', outline="") # 绘制最左侧的柔和红色列 - soft_red_fill = "#F08080" # 亮珊瑚色 - soft_red_line = "#D87070" # 稍暗的亮珊瑚色 - self.canvas.create_rectangle( - 0, - 0, - self.cell_size, - self.canvas_height, - fill=soft_red_fill, - outline="", - ) + soft_red_fill = '#F08080' # 亮珊瑚色 + soft_red_line = '#D87070' # 稍暗的亮珊瑚色 + self.canvas.create_rectangle(0, 0, self.cell_size, self.canvas_height, fill=soft_red_fill, outline="") # 绘制最右侧的柔和蓝色列 - soft_blue_fill = "#ADD8E6" # 浅蓝色 - soft_blue_line = "#9CC2D0" # 稍暗的浅蓝色 - self.canvas.create_rectangle( - self.canvas_width - self.cell_size, - 0, - self.canvas_width, - self.canvas_height, - fill=soft_blue_fill, - outline="", - ) + soft_blue_fill = '#ADD8E6' # 浅蓝色 + soft_blue_line = '#9CC2D0' # 稍暗的浅蓝色 + self.canvas.create_rectangle(self.canvas_width - self.cell_size, 0, self.canvas_width, self.canvas_height, + fill=soft_blue_fill, outline="") for i in range(self.grid_width + 1): x = i * self.cell_size if i == 0 or i == self.grid_width: continue if i == 1: # 红色列的右边缘线 - self.canvas.create_line( - x, 0, x, self.canvas_height, fill=soft_red_line - ) + self.canvas.create_line(x, 0, x, self.canvas_height, fill=soft_red_line) elif i == self.grid_width - 1: # 蓝色列的左边缘线 - self.canvas.create_line( - x, 0, x, self.canvas_height, fill=soft_blue_line - ) + self.canvas.create_line(x, 0, x, self.canvas_height, fill=soft_blue_line) else: - self.canvas.create_line( - x, 0, x, self.canvas_height, fill="lightgray" - ) # 垂直网格线 + self.canvas.create_line(x, 0, x, self.canvas_height, fill='lightgray') # 垂直网格线 # 绘制水平网格线 for i in range(self.grid_height + 1): @@ -478,43 +339,27 @@ def draw_grid(self): # 左边红色区域的水平线可以特殊处理,使其在红色背景上更明显,或者保持lightgray if self.cell_size > 0: # 确保 cell_size > 0 避免问题 # 在红色区域内绘制颜色稍浅的水平线,或者使用对比色 - self.canvas.create_line( - 0, y, self.cell_size, y, fill="#E07070" - ) # 示例:红色区域内的水平线 + self.canvas.create_line(0, y, self.cell_size, y, fill='#E07070') # 示例:红色区域内的水平线 # 在蓝色区域内绘制颜色稍浅的水平线 - self.canvas.create_line( - self.canvas_width - self.cell_size, - y, - self.canvas_width, - y, - fill="#9CBED0", - ) # 示例:蓝色区域内的水平线 + self.canvas.create_line(self.canvas_width - self.cell_size, y, self.canvas_width, y, + fill='#9CBED0') # 示例:蓝色区域内的水平线 # 中间区域的水平线 - self.canvas.create_line( - self.cell_size, - y, - self.canvas_width - self.cell_size, - y, - fill="lightgray", - ) + self.canvas.create_line(self.cell_size, y, self.canvas_width - self.cell_size, y, fill='lightgray') else: # 如果 cell_size 为0或负,则绘制完整线条 - self.canvas.create_line( - 0, y, self.canvas_width, y, fill="lightgray" - ) + self.canvas.create_line(0, y, self.canvas_width, y, fill='lightgray') def refresh_canvas_display(self): - if not self.canvas: - return + if not self.canvas: return self.canvas.delete("all") self.draw_grid() self.canvas.delete("victory_text") # 清除可能存在的胜利信息 if len(self.battle_field.monsters) > len(self.units): for i in range(len(self.units), len(self.battle_field.monsters)): - new_unit = Unit("red", 0, 0, 0) + new_unit = Unit('red', 0, 0, 0) self.units.append(new_unit) elif len(self.battle_field.monsters) < len(self.units): - self.units = self.units[: len(self.battle_field.monsters)] + self.units = self.units[:len(self.battle_field.monsters)] index = 0 for monster in self.battle_field.monsters: @@ -523,11 +368,14 @@ def refresh_canvas_display(self): if monster and monster.is_alive: ui_unit.x = monster.position.x ui_unit.y = monster.position.y - ui_unit.team = ( - "red" if monster.faction == Faction.LEFT else "blue" - ) - ui_unit.unit_id = self._resolve_monster_icon_key(monster.name) - ui_unit.monster_name = monster.name + ui_unit.team = 'red' if monster.faction == Faction.LEFT else 'blue' + display_id_for_icon = REVERSE_MONSTER_MAPPING.get(monster.name) + if display_id_for_icon is None: + logger.error(f"怪物名 {monster.name} 在 REVERSE_MONSTER_MAPPING 中未找到!") + self.show_message_below_button(f"怪物名 {monster.name} 在 REVERSE_MONSTER_MAPPING 中未找到!", + is_error=True) + display_id_for_icon = 0 + ui_unit.unit_id = display_id_for_icon ui_unit.health = monster.health ui_unit.max_health = monster.max_health ui_unit.skill = monster.get_skill_bar() @@ -539,84 +387,42 @@ def refresh_canvas_display(self): for monster_obj in self.battle_field.monsters: if monster_obj.is_alive and monster_obj.target is not None: self.canvas.create_line( - monster_obj.position.x * self.cell_size, - monster_obj.position.y * self.cell_size, - monster_obj.target.position.x * self.cell_size, - monster_obj.target.position.y * self.cell_size, - fill=( - "#FF3030" - if monster_obj.faction == Faction.LEFT - else "#3030FF" - ), - width=1, - arrow="last", - ) + monster_obj.position.x * self.cell_size, monster_obj.position.y * self.cell_size, + monster_obj.target.position.x * self.cell_size, monster_obj.target.position.y * self.cell_size, + fill="#FF3030" if monster_obj.faction == Faction.LEFT else "#3030FF", width=1, arrow='last') self.timer_label.config(text=f"{self.battle_field.gameTime:.2f}秒") - def draw_unit(self, unit, monster: "Monster"): + def draw_unit(self, unit, monster: 'Monster'): x_pixel = unit.x * self.cell_size y_pixel = unit.y * self.cell_size icon_to_draw = None - icon_key = getattr(unit, "unit_id", self.default_icon_key) - if icon_key not in self.icons: - icon_key = self.default_icon_key - - if icon_key in self.icons and unit.team in self.icons[icon_key]: - icon_to_draw = self.icons[icon_key][unit.team] + if unit.unit_id in self.icons and unit.team in self.icons[unit.unit_id]: + icon_to_draw = self.icons[unit.unit_id][unit.team] else: - self.canvas.create_rectangle( - x_pixel - 20, - y_pixel - 20, - x_pixel + 20, - y_pixel + 20, - fill="gray", - tags=("unit",), - ) + self.canvas.create_rectangle(x_pixel - 20, y_pixel - 20, x_pixel + 20, y_pixel + 20, fill="gray", + tags=("unit",)) return - self.canvas.create_image( - x_pixel, y_pixel, image=icon_to_draw, tags=("unit",) - ) + self.canvas.create_image(x_pixel, y_pixel, image=icon_to_draw, tags=("unit",)) bar_width = 40 bar_height = 5 health_bar_y = y_pixel + 25 - health_ratio = ( - min(1, unit.health / unit.max_health) if unit.max_health > 0 else 0 - ) + health_ratio = min(1, unit.health / unit.max_health) if unit.max_health > 0 else 0 current_health_width = max(0, bar_width * health_ratio) - self.canvas.create_rectangle( - x_pixel - bar_width / 2, - health_bar_y - bar_height / 2, - x_pixel + bar_width / 2, - health_bar_y + bar_height / 2, - fill="#400000", - outline="", - ) + self.canvas.create_rectangle(x_pixel - bar_width / 2, health_bar_y - bar_height / 2, x_pixel + bar_width / 2, + health_bar_y + bar_height / 2, fill="#400000", outline="") if current_health_width > 0: - self.canvas.create_rectangle( - x_pixel - bar_width / 2, - health_bar_y - bar_height / 2, - x_pixel - bar_width / 2 + current_health_width, - health_bar_y + bar_height / 2, - fill="#FF3030", - outline="", - ) + self.canvas.create_rectangle(x_pixel - bar_width / 2, health_bar_y - bar_height / 2, + x_pixel - bar_width / 2 + current_health_width, health_bar_y + bar_height / 2, + fill="#FF3030", outline="") skill_bar_y = health_bar_y + 7 - skill_ratio = ( - min(1, unit.skill / unit.max_skill) if unit.max_skill > 0 else 0 - ) + skill_ratio = min(1, unit.skill / unit.max_skill) if unit.max_skill > 0 else 0 current_skill_width = bar_width * skill_ratio - self.canvas.create_rectangle( - x_pixel - bar_width / 2, - skill_bar_y - bar_height / 2, - x_pixel + bar_width / 2, - skill_bar_y + bar_height / 2, - fill="black", - outline="", - ) + self.canvas.create_rectangle(x_pixel - bar_width / 2, skill_bar_y - bar_height / 2, x_pixel + bar_width / 2, + skill_bar_y + bar_height / 2, fill="black", outline="") attack_state_color = "#ffcccc" if monster.attack_state == AttackState.后摇: @@ -625,14 +431,9 @@ def draw_unit(self, unit, monster: "Monster"): attack_state_color = "yellow" if current_skill_width > 0: - self.canvas.create_rectangle( - x_pixel - bar_width / 2, - skill_bar_y - bar_height / 2, - x_pixel - bar_width / 2 + current_skill_width, - skill_bar_y + bar_height / 2, - fill=attack_state_color, - outline="", - ) + self.canvas.create_rectangle(x_pixel - bar_width / 2, skill_bar_y - bar_height / 2, + x_pixel - bar_width / 2 + current_skill_width, skill_bar_y + bar_height / 2, + fill=attack_state_color, outline="") def on_mouse_down(self, event): grid_x = event.x / self.cell_size @@ -641,18 +442,13 @@ def on_mouse_down(self, event): if self.state_machine.state in [AppState.PAUSED, AppState.SETUP]: clicked_monster_obj = None for unit_in_list in reversed(self.units): - monster = self.battle_field.get_monster_with_id( - unit_in_list.monster_global_id - ) - if not monster: - continue + monster = self.battle_field.get_monster_with_id(unit_in_list.monster_global_id) + if not monster: continue monster_center_x_px = monster.position.x * self.cell_size monster_center_y_px = monster.position.y * self.cell_size icon_half_size_px = 20 - if ( - abs(event.x - monster_center_x_px) < icon_half_size_px - and abs(event.y - monster_center_y_px) < icon_half_size_px - ): + if (abs(event.x - monster_center_x_px) < icon_half_size_px and + abs(event.y - monster_center_y_px) < icon_half_size_px): clicked_monster_obj = monster break if clicked_monster_obj: @@ -660,19 +456,14 @@ def on_mouse_down(self, event): return def on_mouse_drag(self, event): - if self.selected_monster_for_drag and self.state_machine.state in [ - AppState.PAUSED, - AppState.SETUP, - ]: + if self.selected_monster_for_drag and self.state_machine.state in [AppState.PAUSED, AppState.SETUP]: new_grid_x = event.x / self.cell_size new_grid_y = event.y / self.cell_size new_grid_x = max(0.25, min(new_grid_x, self.grid_width - 0.25)) new_grid_y = max(0.25, min(new_grid_y, self.grid_height - 0.25)) self.selected_monster_for_drag.position.x = new_grid_x self.selected_monster_for_drag.position.y = new_grid_y - self.selected_monster_for_drag.target = ( - self.selected_monster_for_drag.find_target() - ) + self.selected_monster_for_drag.target = self.selected_monster_for_drag.find_target() self.refresh_canvas_display() def on_mouse_up(self, event): @@ -734,20 +525,14 @@ def simulate(self): # 在画布中央放大显示信息 self.canvas.create_text( - self.canvas_width / 2, - self.canvas_height / 2, - text=game_over_message, - font=font_style, - fill=text_color, - tags="victory_text", + self.canvas_width / 2, self.canvas_height / 2, + text=game_over_message, font=font_style, fill=text_color, tags="victory_text" ) else: interval = max(1, 33) self.simulation_id = self.master.after(interval, self.simulate) - def show_result( - self, message - ): # 此方法现在主要被画布显示和按钮下方提示替代 + def show_result(self, message): # 此方法现在主要被画布显示和按钮下方提示替代 self.show_message_below_button(message) def clear_sandbox(self): @@ -771,9 +556,7 @@ def restore_initial_positions(self): self.show_message_below_button("没有可恢复的初始站位") return self.battle_field = copy.deepcopy(self.initial_battlefield) - self.battle_field.gameTime = ( - self.initial_battlefield.gameTime - ) # 保持时间连续性 + self.battle_field.gameTime = self.initial_battlefield.gameTime # 保持时间连续性 if self.state_machine.state in [AppState.PAUSED, AppState.ENDED]: self.state_machine.transition_to(AppState.SETUP) self.refresh_canvas_display() # 会清除 "victory_text" @@ -784,13 +567,10 @@ def main(): root = tk.Tk() # root.withdraw() # 如果不需要立即隐藏主窗口,可以注释掉 - initial_battle_setup = { - "left": {"Vvan": 4, "炮击组长": 3, "“庞贝”": 2}, - "right": {"大喷蛛": 6, "冰爆源石虫": 23}, - "result": "left", - } + initial_battle_setup = {"left": {"Vvan": 4, "炮击组长": 3, "“庞贝”": 2}, "right": {"大喷蛛": 6, "冰爆源石虫": 23}, + "result": "left"} - sys.stdin.reconfigure(encoding="utf-8") + sys.stdin.reconfigure(encoding='utf-8') try: if not sys.stdin.isatty(): json_data = sys.stdin.read() @@ -802,14 +582,10 @@ def main(): else: logger.info("stdin 是交互式终端,使用默认配置。") except json.JSONDecodeError: - logger.error( - "错误: 无法解析 stdin 中的 JSON 数据,请检查格式,将使用默认配置。" - ) + logger.error("错误: 无法解析 stdin 中的 JSON 数据,请检查格式,将使用默认配置。") except Exception as e: - logger.error( - f"从 stdin 读取或解析时发生未知错误: {e},将使用默认配置。" - ) + logger.error(f"从 stdin 读取或解析时发生未知错误: {e},将使用默认配置。") try: app = SandboxSimulator(root, initial_battle_setup) @@ -823,41 +599,26 @@ def main(): if not app.monster_data: # 检查 monsters.json 是否加载成功 error_messages.append("错误: monsters.json 未找到或为空,请检查文件!") - if ( - len(initial_battle_setup.get("left", {})) == 0 - or len(initial_battle_setup.get("right", {})) == 0 - ): + if len(initial_battle_setup.get("left", {})) == 0 or len(initial_battle_setup.get("right", {})) == 0: error_messages.append("错误:至少一方的怪物列表为空!") problematic_monsters_found = [] - problematic_monster_names = [ - "矿脉守卫", - "提亚卡乌好战者", - "凋零萨卡兹", - "狂暴宿主组长", - "高能源石虫", - ] + problematic_monster_names = ["矿脉守卫", "提亚卡乌好战者", "凋零萨卡兹", "狂暴宿主组长", "高能源石虫"] for team_key in ["left", "right"]: for monster_name in initial_battle_setup.get(team_key, {}): if monster_name in problematic_monster_names: problematic_monsters_found.append( - f"{('左方' if team_key == 'left' else '右方')}存在问题怪物: {monster_name}" - ) + f"{('左方' if team_key == 'left' else '右方')}存在问题怪物: {monster_name}") if problematic_monsters_found: - error_messages.append( - "警告: " + "; ".join(problematic_monsters_found) - ) + error_messages.append("警告: " + "; ".join(problematic_monsters_found)) if error_messages: - app.show_message_below_button( - " | ".join(error_messages), is_error=True, duration=10000 - ) + app.show_message_below_button(" | ".join(error_messages), is_error=True, duration=10000) if "monsters.json 未找到或为空" in " | ".join(error_messages): - logger.error( - "由于 monsters.json 缺失或错误,模拟器可能无法正常工作。" - ) + logger.error("由于 monsters.json 缺失或错误,模拟器可能无法正常工作。") + if not root.winfo_exists(): return @@ -869,10 +630,8 @@ def main(): logging.getLogger().setLevel(logging.DEBUG) logging.getLogger("PIL").setLevel(logging.INFO) stream_handler = logging.StreamHandler() - stream_handler.setFormatter( - logging.Formatter( - "%(asctime)s - %(name)s - %(levelname)s - %(message)s", - ) - ) + stream_handler.setFormatter(logging.Formatter( + "%(asctime)s - %(name)s - %(levelname)s - %(message)s", + )) logging.getLogger().addHandler(stream_handler) main() diff --git a/src/analysis/__init__.py b/models/__init__.py similarity index 100% rename from src/analysis/__init__.py rename to models/__init__.py diff --git a/src/models/model.py b/models/model.py similarity index 94% rename from src/models/model.py rename to models/model.py index 69ad1e4..c55a035 100644 --- a/src/models/model.py +++ b/models/model.py @@ -1,12 +1,9 @@ import torch import torch.nn as nn -from src.core.config import FIELD_FEATURE_COUNT, MONSTER_COUNT - +from config import FIELD_FEATURE_COUNT, MONSTER_COUNT class UnitAwareTransformer(nn.Module): - def __init__( - self, num_units, embed_dim=256, num_heads=4, num_layers=4, dropout=0.3 - ): + def __init__(self, num_units, embed_dim=256, num_heads=4, num_layers=4, dropout=0.3): super().__init__() # num_units,包括怪物种类和场地特征种类 # 怪物特征 + 场地特征 = 总特征数量 @@ -70,12 +67,13 @@ def __init__( nn.init.xavier_uniform_(self.friend_attentions[-1].in_proj_weight) self.norm.append(nn.LayerNorm(embed_dim)) + # 全连接输出层 self.fc = nn.Sequential( nn.Linear(embed_dim, embed_dim * 2), nn.ReLU(), nn.Dropout(dropout), - nn.Linear(embed_dim * 2, 1), + nn.Linear(embed_dim * 2, 1) ) def forward(self, left_signs, left_counts, right_signs, right_counts): @@ -96,7 +94,7 @@ def forward(self, left_signs, left_counts, right_signs, right_counts): left_feat = torch.cat( [ left_feat[..., : embed_dim // 2], # 前x维 - left_feat[..., embed_dim // 2 :] + left_feat[..., embed_dim // 2:] * left_values.unsqueeze(-1), # 后y维乘数量 ], dim=-1, @@ -104,7 +102,7 @@ def forward(self, left_signs, left_counts, right_signs, right_counts): right_feat = torch.cat( [ right_feat[..., : embed_dim // 2], - right_feat[..., embed_dim // 2 :] * right_values.unsqueeze(-1), + right_feat[..., embed_dim // 2:] * right_values.unsqueeze(-1), ], dim=-1, ) diff --git a/src/models/muon.py b/models/muon.py similarity index 75% rename from src/models/muon.py rename to models/muon.py index 1b59250..0e87c93 100644 --- a/src/models/muon.py +++ b/models/muon.py @@ -1,4 +1,5 @@ import torch +import torch.optim as optim class Lion(torch.optim.Optimizer): @@ -27,26 +28,26 @@ def step(self, closure=None): loss = closure() for group in self.param_groups: - for p in group["params"]: + for p in group['params']: if p.grad is None: continue # 首先执行 Weight Decay - p.data.mul_(1 - group["lr"] * group["weight_decay"]) + p.data.mul_(1 - group['lr'] * group['weight_decay']) grad = p.grad state = self.state[p] # 初始化动量状态 if len(state) == 0: - state["exp_avg"] = torch.zeros_like(p) + state['exp_avg'] = torch.zeros_like(p) - exp_avg = state["exp_avg"] - beta1, beta2 = group["betas"] + exp_avg = state['exp_avg'] + beta1, beta2 = group['betas'] # 计算 Lion 特有的更新:符号动量 update = exp_avg * beta1 + grad * (1 - beta1) - p.add_(torch.sign(update), alpha=-group["lr"]) + p.add_(torch.sign(update), alpha=-group['lr']) # 更新 EMA 动量 exp_avg.mul_(beta2).add_(grad, alpha=1 - beta2) @@ -59,15 +60,8 @@ class Muon(torch.optim.Optimizer): 根据《Muon is Scalable for LLM Training》技术报告实现的 Muon 优化器。 """ - def __init__( - self, params, lr=1e-3, momentum=0.95, weight_decay=0.1, ns_steps=5 - ): - defaults = dict( - lr=lr, - momentum=momentum, - weight_decay=weight_decay, - ns_steps=ns_steps, - ) + def __init__(self, params, lr=1e-3, momentum=0.95, weight_decay=0.1, ns_steps=5): + defaults = dict(lr=lr, momentum=momentum, weight_decay=weight_decay, ns_steps=ns_steps) super().__init__(params, defaults) @torch.no_grad() @@ -78,23 +72,23 @@ def step(self, closure=None): loss = closure() for group in self.param_groups: - lr = group["lr"] - momentum = group["momentum"] - weight_decay = group["weight_decay"] - ns_steps = group["ns_steps"] + lr = group['lr'] + momentum = group['momentum'] + weight_decay = group['weight_decay'] + ns_steps = group['ns_steps'] - for p in group["params"]: + for p in group['params']: if p.grad is None: continue grad = p.grad if grad.is_sparse: - raise RuntimeError("Muon 不支持稀疏梯度计算") + raise RuntimeError('Muon 不支持稀疏梯度计算') state = self.state[p] if len(state) == 0: - state["momentum_buffer"] = torch.zeros_like(grad) + state['momentum_buffer'] = torch.zeros_like(grad) - buf = state["momentum_buffer"] + buf = state['momentum_buffer'] # 应用基于 Nesterov 的动量: M_t = \mu * M_{t-1} + \nabla L_t buf.mul_(momentum).add_(grad) @@ -124,7 +118,7 @@ def step(self, closure=None): dim0, dim1 = X.size(0), X.size(1) if X.ndim > 1 else 1 max_dim = max(dim0, dim1) - scale = 0.2 * (max_dim**0.5) + scale = 0.2 * (max_dim ** 0.5) p.data.mul_(1 - lr * weight_decay) p.data.add_(X, alpha=-lr * scale) @@ -132,9 +126,7 @@ def step(self, closure=None): return loss -def get_muon_lion_optimizers( - model, muon_lr, lion_lr, weight_decay=0.1, muon_momentum=0.95 -): +def get_muon_lion_optimizers(model, muon_lr, lion_lr, weight_decay=0.1, muon_momentum=0.95): """ 提供 Muon + Lion 的组合优化器分发策略。 对模型的所有 >=2D 参数采用 Muon; @@ -148,17 +140,12 @@ def get_muon_lion_optimizers( if not p.requires_grad: continue - if p.ndim >= 2 and "embed" not in name.lower(): + if p.ndim >= 2 and 'embed' not in name.lower(): muon_params.append(p) else: lion_params.append(p) - muon_opt = Muon( - muon_params, - lr=muon_lr, - momentum=muon_momentum, - weight_decay=weight_decay, - ) + muon_opt = Muon(muon_params, lr=muon_lr, momentum=muon_momentum, weight_decay=weight_decay) # Lion 需要稍微大一点的 weight_decay 以防止过拟合,通常建议比 AdamW 大 3-10 倍 # 这里我们将其设置为常规 weight_decay 的 3 倍 diff --git a/models/update.md b/models/update.md new file mode 100644 index 0000000..fd552e3 --- /dev/null +++ b/models/update.md @@ -0,0 +1,7 @@ +# 这个文件用来放模型更新日志(如果有人想写的话) +---- +2025/5/6: + +- 数据库:dataset56f_64k +- 超参数:256/4/8/5e-4/30/1145 +- 准确率:93.23%/89.32% \ No newline at end of file diff --git a/src/resources/data/monster.csv b/monster.csv similarity index 100% rename from src/resources/data/monster.csv rename to monster.csv diff --git a/src/resources/data/monster_greenvine.csv b/monster_greenvine.csv similarity index 100% rename from src/resources/data/monster_greenvine.csv rename to monster_greenvine.csv diff --git a/multi_instance.py b/multi_instance.py index 4f74b93..b9307c2 100644 --- a/multi_instance.py +++ b/multi_instance.py @@ -5,61 +5,47 @@ import subprocess from pathlib import Path from PyQt6.QtWidgets import ( - QApplication, - QMainWindow, - QWidget, - QVBoxLayout, - QHBoxLayout, - QPushButton, - QLabel, - QPlainTextEdit, - QComboBox, - QCheckBox, - QMessageBox, - QSplitter, - QScrollArea, - QFrame, - QLineEdit, + QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, + QPushButton, QLabel, QPlainTextEdit, QSpinBox, QComboBox, QCheckBox, + QMessageBox, QSplitter, QScrollArea, QFrame, QLineEdit ) -from PyQt6.QtCore import Qt, QTimer, pyqtSignal +from PyQt6.QtCore import Qt, QTimer, pyqtSignal, QObject, pyqtSlot from PyQt6.QtGui import QFont -from src.data import load_data -from src.game import auto_fetch -from src.data import data_package -from src.game.login import LoginManager -from src.core.config import FIELD_FEATURE_COUNT +import loadData +import auto_fetch +import data_package +from recognize import MONSTER_COUNT +from login import LoginManager +from config import FIELD_FEATURE_COUNT class LogDisplay(QPlainTextEdit): log_signal = pyqtSignal(str) - + def __init__(self, parent=None): super().__init__(parent) self.setReadOnly(True) self._auto_scroll = True self.log_signal.connect(self._on_log) - + def is_at_bottom(self): scrollbar = self.verticalScrollBar() return scrollbar.value() >= scrollbar.maximum() - 10 - + def scrollContentsBy(self, dx, dy): super().scrollContentsBy(dx, dy) self._auto_scroll = self.is_at_bottom() - + def _on_log(self, text): was_at_bottom = self._auto_scroll self.appendPlainText(text) if was_at_bottom: - self.verticalScrollBar().setValue( - self.verticalScrollBar().maximum() - ) - + self.verticalScrollBar().setValue(self.verticalScrollBar().maximum()) + def append_log(self, text): self.log_signal.emit(text) - logging.getLogger().setLevel(logging.DEBUG) logging.getLogger("PIL").setLevel(logging.INFO) stream_handler = logging.StreamHandler() @@ -74,7 +60,6 @@ def append_log(self, text): class SmartPortsLineEdit(QLineEdit): """智能端口输入框:支持延迟格式化、失去焦点格式化和粘贴立即格式化""" - def __init__(self, parent=None): super().__init__(parent) self.format_timer = QTimer(self) @@ -99,26 +84,19 @@ def _format_text(self): if self._formatting: return self._formatting = True - + text = self.text() - parts = ( - text.replace("\n", ",") - .replace(",", ",") - .replace(";", ",") - .replace(" ", ",") - .split(",") - ) + parts = text.replace('\n', ',').replace(',', ',').replace(';', ',').replace(' ', ',').split(',') ports = [p.strip() for p in parts if p.strip().isdigit()] formatted = ", ".join(ports) - + if text != formatted and ports: cursor_pos = self.cursorPosition() self.setText(formatted) self.setCursorPosition(min(cursor_pos, len(formatted))) - + self._formatting = False - # 用于共享资源密集型对象,减少多开时的内存占用 _cannot_model = None _recognizer = None @@ -131,14 +109,12 @@ def get_cannot_model(): if _cannot_model is None: logger.info("首次初始化 CannotModel...") try: - from src.models.predict import CannotModel - + from predict import CannotModel logger.info("Using PyTorch model for predictions.") except Exception: - from src.models.predict_onnx import CannotModel - + from predict_onnx import CannotModel logger.info("Using ONNX model for predictions.") - + _cannot_model = CannotModel() logger.info("CannotModel 初始化完成") return _cannot_model @@ -149,8 +125,7 @@ def get_recognizer(): global _recognizer if _recognizer is None: logger.info("首次初始化 RecognizeMonster...") - from src.recognition.recognize import RecognizeMonster - + from recognize import RecognizeMonster _recognizer = RecognizeMonster(method="ADB") logger.info("RecognizeMonster 初始化完成") return _recognizer @@ -161,8 +136,7 @@ def get_field_recognizer(): global _field_recognizer if _field_recognizer is None: logger.info("首次初始化 FieldRecognizer...") - from src.recognition.field_recognition import FieldRecognizer - + from field_recognition import FieldRecognizer _field_recognizer = FieldRecognizer() logger.info("FieldRecognizer 初始化完成") return _field_recognizer @@ -181,14 +155,12 @@ class DeviceInstance: def __init__(self, port): self.port = port self.serial = f"127.0.0.1:{port}" - self.connector = load_data.AdbConnector(self.serial) + self.connector = loadData.AdbConnector(self.serial) self.auto_fetch = None self.login_manager = None self.status = "已停止" self.auto_fetch_thread = None # 保存线程引用 - self.stop_event = ( - threading.Event() - ) # 使用Event对象进行线程间通信,更可靠 + self.stop_event = threading.Event() # 使用Event对象进行线程间通信,更可靠 self.start_time = None # 实例启动时间戳 self.last_activity_time = time.time() # 最后活动时间戳(用于检测崩溃) self.thread_running = False # 线程是否正在运行的标志 @@ -200,56 +172,49 @@ def start(self, game_mode, is_invest): # 保存设置 self.game_mode = game_mode self.is_invest = is_invest - + # 重置停止事件和标志 self.stop_event.clear() self.thread_running = True self.last_activity_time = time.time() self.status = "连接中" - - logger.info( - f"[{self.serial}] 开始启动实例,游戏模式: {game_mode}, 自动投资: {is_invest}" - ) - + + logger.info(f"[{self.serial}] 开始启动实例,游戏模式: {game_mode}, 自动投资: {is_invest}") + # 记录实例启动时间戳 self.start_time = time.time() - + self.connector.connect() if not self.connector.is_connected: self.status = "连接失败" logger.error(f"[{self.serial}] 连接失败") self.thread_running = False return False - + self.login_manager = LoginManager(self.connector) logger.info(f"[{self.serial}] 尝试首次启动自动登录") self.status = "登录中" - login_success = self.login_manager.auto_login_with_restart( - first_start=True, - stop_callback=lambda: not self.stop_event.is_set(), - ) - + login_success = self.login_manager.auto_login_with_restart(first_start=True, stop_callback=lambda: not self.stop_event.is_set()) + # 检查是否在登录过程中用户点击了停止 if self.stop_event.is_set(): logger.info(f"[{self.serial}] 登录过程被用户停止") self.status = "已停止" self.thread_running = False return False - + if login_success: logger.info(f"[{self.serial}] 首次启动自动登录成功") else: - logger.warning( - f"[{self.serial}] 首次启动自动登录失败,继续启动" - ) - + logger.warning(f"[{self.serial}] 首次启动自动登录失败,继续启动") + # 再次检查停止标志(防止在登录成功后、创建auto_fetch前用户点击停止) if self.stop_event.is_set(): logger.info(f"[{self.serial}] 用户在登录成功后点击了停止") self.status = "已停止" self.thread_running = False return False - + self.auto_fetch = auto_fetch.AutoFetch( self.connector, game_mode, @@ -262,9 +227,7 @@ def start(self, game_mode, is_invest): training_duration=-1, recognizer=get_recognizer(), cannot_model=get_cannot_model(), - field_recognizer=( - get_field_recognizer() if FIELD_FEATURE_COUNT > 0 else None - ), + field_recognizer=get_field_recognizer() if FIELD_FEATURE_COUNT > 0 else None, start_timestamp=self.start_time, # 传递实例启动时间戳 ) logger.info(f"[{self.serial}] 初始化 AutoFetch 成功") @@ -277,11 +240,11 @@ def start(self, game_mode, is_invest): logger.error(f"[{self.serial}] 启动失败: {str(e)}") self.thread_running = False return False - + def _update_activity_time(self, *args, **kwargs): """更新活动时间""" self.last_activity_time = time.time() - + def _on_stop_callback(self): """当 auto_fetch 停止时的回调""" self.thread_running = False @@ -292,7 +255,7 @@ def stop(self): # 设置停止事件,用于中断登录流程 self.stop_event.set() self.thread_running = False - + if self.auto_fetch: # 强制设置停止标志,不等待线程退出 self.auto_fetch.auto_fetch_running = False @@ -300,7 +263,7 @@ def stop(self): else: # 如果 auto_fetch 还未创建,说明可能正在登录过程中 logger.info(f"[{self.serial}] auto_fetch 尚未创建,停止登录过程") - + # 状态改为已停止(stop_event 会在下次启动时自动清除) self.status = "已停止" logger.info(f"[{self.serial}] 强制停止成功,状态: {self.status}") @@ -308,74 +271,67 @@ def stop(self): def get_status_line(self): if not self.auto_fetch or not self.auto_fetch.auto_fetch_running: return f"[{self.serial:<15}] 状态: {self.status}" - + af = self.auto_fetch elapsed = time.time() - af.start_time if af.start_time else 0 hours, remainder = divmod(elapsed, 3600) minutes, _ = divmod(remainder, 60) - + state_name = "过场动画" - if hasattr(af, "last_state") and af.last_state: - state_name = ( - af.last_state.name - if hasattr(af.last_state, "name") - else str(af.last_state) - ) - - return ( - f"[{self.serial:<15}] " - f"状态: {state_name:<8} | " - f"填写: {af.total_fill_count:<3} | " - f"错误: {af.incorrect_fill_count:<3} | " - f"预测: {af.current_prediction:.2f} | " - f"时长: {int(hours)}h {int(minutes)}m" - ) - + if hasattr(af, 'last_state') and af.last_state: + state_name = af.last_state.name if hasattr(af.last_state, 'name') else str(af.last_state) + + return (f"[{self.serial:<15}] " + f"状态: {state_name:<8} | " + f"填写: {af.total_fill_count:<3} | " + f"错误: {af.incorrect_fill_count:<3} | " + f"预测: {af.current_prediction:.2f} | " + f"时长: {int(hours)}h {int(minutes)}m") class MultiInstanceManager(QMainWindow): instance = None # 类变量,用于在回调函数中引用当前实例 - + def __init__(self): super().__init__() MultiInstanceManager.instance = self # 保存当前实例的引用 self.setWindowTitle("铁鲨鱼多开自动化工具") self.setGeometry(100, 100, 530, 720) - + self.instances = {} self.starting_ports = set() # 正在启动中的端口集合,防止重复启动 self.init_ui() self.setup_logger() - + # 定时更新界面 self.timer = QTimer() self.timer.timeout.connect(self.update_display) self.timer.start(1000) - + # 崩溃检测定时器(每 5 秒检查一次) self.crash_detection_timer = QTimer() self.crash_detection_timer.timeout.connect(self.check_instances_crash) self.crash_detection_timer.start(5000) - + def init_ui(self): central_widget = QWidget() self.setCentralWidget(central_widget) layout = QVBoxLayout(central_widget) - + # 配置区域 settings_layout = QHBoxLayout() - + self.game_mode_combo = QComboBox() self.game_mode_combo.addItems(["单人", "30人"]) settings_layout.addWidget(QLabel("模式:")) settings_layout.addWidget(self.game_mode_combo) - + self.invest_check = QCheckBox("自动投资") self.invest_check.setChecked(False) settings_layout.addWidget(self.invest_check) settings_layout.addStretch() - + layout.addLayout(settings_layout) - + # 端口输入(横向,逗号分隔) ports_layout = QHBoxLayout() ports_layout.addWidget(QLabel("端口:")) @@ -390,7 +346,7 @@ def init_ui(self): pass ports_layout.addWidget(self.ports_input) layout.addLayout(ports_layout) - + # 按钮和端口选择器 btn_layout = QHBoxLayout() self.start_btn = QPushButton("全部启动") @@ -399,26 +355,26 @@ def init_ui(self): self.stop_btn.clicked.connect(self.stop_all) self.package_btn = QPushButton("打包数据") self.package_btn.clicked.connect(self.package_data) - + # 端口选择器,用于选择要查看哪个端口的日志 self.port_combo = QComboBox() self.port_combo.addItem("全部端口") self.port_combo.currentTextChanged.connect(self.update_log_filter) - + btn_layout.addWidget(self.start_btn) btn_layout.addWidget(self.stop_btn) btn_layout.addWidget(self.package_btn) btn_layout.addWidget(QLabel("日志过滤:")) btn_layout.addWidget(self.port_combo) layout.addLayout(btn_layout) - + font = QFont("Courier New", 10) if sys.platform == "win32": font = QFont("Consolas", 10) - + # 使用 QSplitter 让状态和日志区域可以自由拖动调整大小 splitter = QSplitter(Qt.Orientation.Vertical) - + # 状态显示区域(带单独控制按钮的滚动区域) self.status_scroll = QScrollArea() self.status_scroll.setWidgetResizable(True) @@ -431,29 +387,23 @@ def init_ui(self): self.status_scroll.setWidget(self.status_container) self.status_scroll.setMinimumHeight(80) splitter.addWidget(self.status_scroll) - + # 日志显示 self.log_display = LogDisplay() self.log_display.setFont(font) self.log_display.setMaximumBlockCount(2000) splitter.addWidget(self.log_display) - + splitter.setStretchFactor(0, 1) splitter.setStretchFactor(1, 3) splitter.setSizes([440, 330]) layout.addWidget(splitter) - + self.port_widgets = {} @staticmethod def _parse_ports(text): - parts = ( - text.replace("\n", ",") - .replace(",", ",") - .replace(";", ",") - .replace(" ", ",") - .split(",") - ) + parts = text.replace('\n', ',').replace(',', ',').replace(';', ',').replace(' ', ',').split(',') return [p.strip() for p in parts if p.strip().isdigit()] def start_all(self): @@ -462,15 +412,13 @@ def start_all(self): logger.info("保存端口配置到 multi_ports.txt") except Exception as e: logger.error(f"保存端口配置失败: {str(e)}") - + ports = self._parse_ports(self.ports_input.text()) game_mode = self.game_mode_combo.currentText() is_invest = self.invest_check.isChecked() - - logger.info( - f"开始启动多开实例,端口列表: {ports}, 游戏模式: {game_mode}, 自动投资: {is_invest}" - ) - + + logger.info(f"开始启动多开实例,端口列表: {ports}, 游戏模式: {game_mode}, 自动投资: {is_invest}") + def start_single_instance(port): # 检查是否已经在运行或正在启动中 is_running = False @@ -478,12 +426,12 @@ def start_single_instance(port): inst = self.instances[port] if inst.auto_fetch and inst.auto_fetch.auto_fetch_running: is_running = True - + # 使用启动锁防止重复启动 if port in self.starting_ports: logger.info(f"端口 {port} 的实例正在启动中,跳过重复启动") return - + if not is_running: # 添加到启动中集合 self.starting_ports.add(port) @@ -501,21 +449,19 @@ def start_single_instance(port): self.starting_ports.discard(port) else: logger.info(f"端口 {port} 的实例已经在运行,跳过启动") - + def start_all_instances(): threads = [] for port in ports: - t = threading.Thread( - target=start_single_instance, args=(port,), daemon=True - ) + t = threading.Thread(target=start_single_instance, args=(port,), daemon=True) t.start() threads.append(t) time.sleep(3) for t in threads: t.join() - + threading.Thread(target=start_all_instances, daemon=True).start() - + def stop_all(self): logger.info("开始停止所有实例") # 清空启动中集合,防止正在启动的实例继续运行 @@ -531,16 +477,10 @@ def package_data(self): zip_filename = data_package.package_data() if zip_filename and Path(zip_filename).exists(): # 在文件浏览器中高亮显示文件 - subprocess.run( - f'explorer /select,"{Path(zip_filename).absolute()}"' - ) - QMessageBox.information( - self, "成功", f"数据已打包到 {zip_filename}" - ) + subprocess.run(f'explorer /select,"{Path(zip_filename).absolute()}"') + QMessageBox.information(self, "成功", f"数据已打包到 {zip_filename}") else: - QMessageBox.warning( - self, "警告", "没有找到可以打包的数据目录或打包失败。" - ) + QMessageBox.warning(self, "警告", "没有找到可以打包的数据目录或打包失败。") except Exception as e: QMessageBox.critical(self, "错误", f"打包数据时发生错误: {str(e)}") @@ -549,57 +489,43 @@ class QTextEditLogger(logging.Handler): def __init__(self, text_edit): super().__init__() self.text_edit = text_edit - self.setFormatter( - logging.Formatter( - "%(asctime)s - %(levelname)s - %(message)s" - ) - ) + self.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')) self.target_port = None self.log_history = [] - + def set_target_port(self, port): self.target_port = port - + def emit(self, record): try: msg = self.format(record) except Exception: return - + self.log_history.append(msg) if len(self.log_history) > 2000: self.log_history.pop(0) - + if self.target_port: port_str = str(self.target_port) - if not ( - f"[127.0.0.1:{port_str}]" in msg - or f"[{port_str}]" in msg - or f"端口 {port_str}" in msg - ): + if not (f"[127.0.0.1:{port_str}]" in msg or f"[{port_str}]" in msg or f"端口 {port_str}" in msg): return - + if self.text_edit is not None: self.text_edit.append_log(msg) - + root_logger = logging.getLogger() self.text_edit_logger = QTextEditLogger(self.log_display) self.text_edit_logger.setLevel(logging.INFO) root_logger.addHandler(self.text_edit_logger) root_logger.setLevel(logging.INFO) - + # 确保子模块日志传播到 root logger - for name in [ - "login", - "auto_fetch", - "recognize", - "multi_instance", - "__main__", - ]: + for name in ['login', 'auto_fetch', 'recognize', 'multi_instance', '__main__']: child_logger = logging.getLogger(name) child_logger.setLevel(logging.INFO) child_logger.propagate = True - + def update_log_filter(self, text): if text == "全部端口": self.text_edit_logger.set_target_port(None) @@ -612,11 +538,10 @@ def update_log_filter(self, text): for msg in self.text_edit_logger.log_history: if f"[{text}]" in msg or f"端口 {text}" in msg or text in msg: self.log_display.append_log(msg) - + @staticmethod def _state_to_chinese(state): - from src.game.auto_fetch import GameState - + from auto_fetch import GameState state_map = { GameState.MAIN_MENU: "主页", GameState.MODE_SELECTION_UNSELECTED: "模式", @@ -628,46 +553,36 @@ def _state_to_chinese(state): GameState.UNKNOWN: "过场", } return state_map.get(state, "过场动画") - + def _create_port_widget(self, port): row = QHBoxLayout() row.setContentsMargins(4, 2, 4, 2) row.setSpacing(6) - + label = QLabel(port) label.setFixedWidth(60) - label.setFont( - QFont("Consolas", 10) - if sys.platform == "win32" - else QFont("Courier New", 10) - ) + label.setFont(QFont("Consolas", 10) if sys.platform == "win32" else QFont("Courier New", 10)) row.addWidget(label) - + status_label = QLabel("已停止") status_label.setFixedWidth(80) row.addWidget(status_label) - + detail_label = QLabel("") - detail_label.setFont( - QFont("Consolas", 9) - if sys.platform == "win32" - else QFont("Courier New", 9) - ) + detail_label.setFont(QFont("Consolas", 9) if sys.platform == "win32" else QFont("Courier New", 9)) row.addWidget(detail_label, 1) - + toggle_btn = QPushButton("启动") toggle_btn.setFixedWidth(50) - toggle_btn.clicked.connect( - lambda checked, p=port: self._toggle_port(p) - ) + toggle_btn.clicked.connect(lambda checked, p=port: self._toggle_port(p)) row.addWidget(toggle_btn) - + frame = QFrame() frame.setLayout(row) frame.setFrameShape(QFrame.Shape.StyledPanel) - + return frame, status_label, detail_label, toggle_btn - + def _toggle_port(self, port): if port in self.instances: instance = self.instances[port] @@ -679,7 +594,7 @@ def _toggle_port(self, port): else: game_mode = self.game_mode_combo.currentText() is_invest = self.invest_check.isChecked() - + def do_start(): if port in self.starting_ports: return @@ -690,66 +605,57 @@ def do_start(): instance.start(game_mode, is_invest) finally: self.starting_ports.discard(port) - + threading.Thread(target=do_start, daemon=True).start() - + def check_instances_crash(self): """检查实例是否崩溃,并尝试自动恢复""" current_time = time.time() - + # 遍历所有实例,检查崩溃情况 for port, instance in list(self.instances.items()): # 如果已经设置了停止事件,就不进行崩溃检测 if instance.stop_event.is_set(): continue - + # 检查活动时间(崩溃检测) inactive_time = current_time - instance.last_activity_time - + # 如果标记为运行但超过 3 分钟没有活动,视为崩溃 - if ( - instance.status in ["正在运行", "连接中", "登录中"] - and inactive_time > 180 - ): - logger.warning( - f"[{instance.serial}] 检测到无活动超过 {inactive_time:.0f} 秒,可能已崩溃,尝试重启" - ) + if instance.status in ["正在运行", "连接中", "登录中"] and inactive_time > 180: + logger.warning(f"[{instance.serial}] 检测到无活动超过 {inactive_time:.0f} 秒,可能已崩溃,尝试重启") self._restart_crashed_instance(port, instance) # 检查 auto_fetch 线程是否实际还在运行 elif instance.status == "正在运行" and instance.auto_fetch: if not instance.auto_fetch.auto_fetch_running: - logger.warning( - f"[{instance.serial}] auto_fetch_running 为 False,可能已意外停止" - ) + logger.warning(f"[{instance.serial}] auto_fetch_running 为 False,可能已意外停止") self._restart_crashed_instance(port, instance) - + def _restart_crashed_instance(self, port, instance): """重启崩溃的实例""" try: # 保存设置 game_mode = instance.game_mode if instance.game_mode else "30人" - is_invest = ( - instance.is_invest if instance.is_invest is not None else False - ) - + is_invest = instance.is_invest if instance.is_invest is not None else False + # 先停止(强制) instance.stop() - + # 从字典中清除 if port in self.instances: del self.instances[port] - + # 延迟重新启动 logger.info(f"[{instance.serial}] 准备重新启动...") - + def restart_task(): if port in self.starting_ports: logger.warning(f"端口 {port} 已在启动中,跳过重启") return - + logger.info(f"[{instance.serial}] 正在重新启动...") self.starting_ports.add(port) - + try: new_instance = DeviceInstance(port) self.instances[port] = new_instance @@ -760,108 +666,98 @@ def restart_task(): logger.error(f"[{instance.serial}] 崩溃后重启失败") finally: self.starting_ports.discard(port) - + threading.Thread(target=restart_task, daemon=True).start() except Exception as e: logger.error(f"重启崩溃实例时出错: {str(e)}") - + def update_display(self): input_ports = self._parse_ports(self.ports_input.text()) - - if not hasattr(self, "_last_input_ports"): + + if not hasattr(self, '_last_input_ports'): self._last_input_ports = [] - + ports_changed = input_ports != self._last_input_ports - + if ports_changed: current_text = self.port_combo.currentText() self.port_combo.clear() self.port_combo.addItem("全部端口") for port in input_ports: self.port_combo.addItem(port) - + if input_ports: if current_text in input_ports: self.port_combo.setCurrentText(current_text) else: self.port_combo.setCurrentIndex(1) self._last_input_ports = input_ports.copy() - + # 重建端口控件 for w in self.port_widgets.values(): - w["frame"].setParent(None) + w['frame'].setParent(None) self.port_widgets.clear() - + for i in range(self.status_layout.count() - 1): item = self.status_layout.itemAt(i) if item.widget(): item.widget().setParent(None) - + for port in input_ports: - frame, status_label, detail_label, toggle_btn = ( - self._create_port_widget(port) - ) + frame, status_label, detail_label, toggle_btn = self._create_port_widget(port) idx = self.status_layout.count() - 1 self.status_layout.insertWidget(idx, frame) self.port_widgets[port] = { - "frame": frame, - "status_label": status_label, - "detail_label": detail_label, - "toggle_btn": toggle_btn, + 'frame': frame, + 'status_label': status_label, + 'detail_label': detail_label, + 'toggle_btn': toggle_btn, } - + any_running = False for port in input_ports: if port not in self.port_widgets: continue - + widgets = self.port_widgets[port] - + if port in self.instances: instance = self.instances[port] - is_running = ( - instance.auto_fetch - and instance.auto_fetch.auto_fetch_running - ) - + is_running = instance.auto_fetch and instance.auto_fetch.auto_fetch_running + if is_running: any_running = True af = instance.auto_fetch - elapsed = ( - time.time() - af.start_time if af.start_time else 0 - ) + elapsed = time.time() - af.start_time if af.start_time else 0 hours, remainder = divmod(elapsed, 3600) minutes, _ = divmod(remainder, 60) - + state_name = "过场动画" - if hasattr(af, "last_state") and af.last_state: + if hasattr(af, 'last_state') and af.last_state: state_name = self._state_to_chinese(af.last_state) - - widgets["status_label"].setText(f"运行中·{state_name}") - widgets["detail_label"].setText( + + widgets['status_label'].setText(f"运行中·{state_name}") + widgets['detail_label'].setText( f"填写: {af.total_fill_count} | 错误: {af.incorrect_fill_count} | " f"预测: {af.current_prediction:.2f} | 时长: {int(hours)}h {int(minutes)}m" ) - widgets["toggle_btn"].setText("停止") + widgets['toggle_btn'].setText("停止") else: - widgets["status_label"].setText(instance.status) - widgets["detail_label"].setText("") + widgets['status_label'].setText(instance.status) + widgets['detail_label'].setText("") is_starting = instance.status in ("连接中", "登录中") - widgets["toggle_btn"].setText( - "停止" if is_starting else "启动" - ) + widgets['toggle_btn'].setText("停止" if is_starting else "启动") else: - widgets["status_label"].setText("已停止") - widgets["detail_label"].setText("") - widgets["toggle_btn"].setText("启动") - + widgets['status_label'].setText("已停止") + widgets['detail_label'].setText("") + widgets['toggle_btn'].setText("启动") + self.package_btn.setEnabled(not any_running) def closeEvent(self, event): self.stop_all() event.accept() - if __name__ == "__main__": app = QApplication(sys.argv) window = MultiInstanceManager() diff --git a/vendor/bin/platform-tools/AdbWinApi.dll b/platform-tools/AdbWinApi.dll similarity index 100% rename from vendor/bin/platform-tools/AdbWinApi.dll rename to platform-tools/AdbWinApi.dll diff --git a/vendor/bin/platform-tools/AdbWinUsbApi.dll b/platform-tools/AdbWinUsbApi.dll similarity index 100% rename from vendor/bin/platform-tools/AdbWinUsbApi.dll rename to platform-tools/AdbWinUsbApi.dll diff --git a/vendor/bin/platform-tools/NOTICE.txt b/platform-tools/NOTICE.txt similarity index 100% rename from vendor/bin/platform-tools/NOTICE.txt rename to platform-tools/NOTICE.txt diff --git a/vendor/bin/platform-tools/adb.exe b/platform-tools/adb.exe similarity index 100% rename from vendor/bin/platform-tools/adb.exe rename to platform-tools/adb.exe diff --git a/vendor/bin/platform-tools/etc1tool.exe b/platform-tools/etc1tool.exe similarity index 100% rename from vendor/bin/platform-tools/etc1tool.exe rename to platform-tools/etc1tool.exe diff --git a/vendor/bin/platform-tools/fastboot.exe b/platform-tools/fastboot.exe similarity index 100% rename from vendor/bin/platform-tools/fastboot.exe rename to platform-tools/fastboot.exe diff --git a/vendor/bin/platform-tools/hprof-conv.exe b/platform-tools/hprof-conv.exe similarity index 100% rename from vendor/bin/platform-tools/hprof-conv.exe rename to platform-tools/hprof-conv.exe diff --git a/vendor/bin/platform-tools/libwinpthread-1.dll b/platform-tools/libwinpthread-1.dll similarity index 100% rename from vendor/bin/platform-tools/libwinpthread-1.dll rename to platform-tools/libwinpthread-1.dll diff --git a/vendor/bin/platform-tools/make_f2fs.exe b/platform-tools/make_f2fs.exe similarity index 100% rename from vendor/bin/platform-tools/make_f2fs.exe rename to platform-tools/make_f2fs.exe diff --git a/vendor/bin/platform-tools/make_f2fs_casefold.exe b/platform-tools/make_f2fs_casefold.exe similarity index 100% rename from vendor/bin/platform-tools/make_f2fs_casefold.exe rename to platform-tools/make_f2fs_casefold.exe diff --git a/vendor/bin/platform-tools/mke2fs.conf b/platform-tools/mke2fs.conf similarity index 100% rename from vendor/bin/platform-tools/mke2fs.conf rename to platform-tools/mke2fs.conf diff --git a/vendor/bin/platform-tools/mke2fs.exe b/platform-tools/mke2fs.exe similarity index 100% rename from vendor/bin/platform-tools/mke2fs.exe rename to platform-tools/mke2fs.exe diff --git a/vendor/bin/platform-tools/source.properties b/platform-tools/source.properties similarity index 100% rename from vendor/bin/platform-tools/source.properties rename to platform-tools/source.properties diff --git a/vendor/bin/platform-tools/sqlite3.exe b/platform-tools/sqlite3.exe similarity index 100% rename from vendor/bin/platform-tools/sqlite3.exe rename to platform-tools/sqlite3.exe diff --git a/src/models/predict.py b/predict.py similarity index 61% rename from src/models/predict.py rename to predict.py index 8e14cc4..59edb44 100644 --- a/src/models/predict.py +++ b/predict.py @@ -1,6 +1,4 @@ import re -import sys -import importlib from datetime import datetime from functools import cache from pathlib import Path @@ -9,37 +7,11 @@ import torch import logging -from src.core.config import MONSTER_COUNT -from src.core.config import FIELD_FEATURE_COUNT +from config import MONSTER_COUNT +from config import FIELD_FEATURE_COUNT logger = logging.getLogger(__name__) - -def _register_legacy_model_aliases(): - """ - 为旧版 pickle 模型提供模块路径兼容: - 例如历史权重里记录的是 `models.model`,而当前代码已迁移到 `src.models.model`。 - """ - legacy_to_new = { - "models": "src.models", - "models.model": "src.models.model", - "models.muon": "src.models.muon", - } - - for legacy_name, new_name in legacy_to_new.items(): - if legacy_name in sys.modules: - continue - try: - sys.modules[legacy_name] = importlib.import_module(new_name) - except Exception as e: - logger.debug( - "兼容模块别名注册失败: %s -> %s, %s", - legacy_name, - new_name, - e, - ) - - def get_device(prefer_gpu=True): """ prefer_gpu (bool): 是否优先尝试使用GPU @@ -48,10 +20,7 @@ def get_device(prefer_gpu=True): if torch.cuda.is_available(): logger.info("Use torch with cuda") return torch.device("cuda") - elif ( - hasattr(torch.backends, "mps") - and torch.backends.mps.is_available() - ): + elif hasattr(torch.backends, "mps") and torch.backends.mps.is_available(): logger.info("Use torch with mps") return torch.device("mps") # Apple Silicon GPU elif hasattr(torch, "xpu") and torch.xpu.is_available(): # Intel GPU @@ -60,7 +29,6 @@ def get_device(prefer_gpu=True): logger.info("Use torch with cpu") return torch.device("cpu") - class CannotModel: def __init__(self, model_path="models"): self.device = get_device() @@ -81,11 +49,7 @@ def _resolve_model_path(self, path): if Path(path).is_dir(): logger.info(f"Searching for the latest model in directory: {path}") model_dir = Path(path) - models = [ - f - for f in model_dir.iterdir() - if f.suffix == ".pth" and f.is_file() - ] + models = [f for f in model_dir.iterdir() if f.suffix == ".pth" and f.is_file()] if not models: logger.error(f"No model files (.pth) found in {path}") @@ -100,20 +64,12 @@ def _resolve_model_path(self, path): match = pattern.match(model_file_path.name) if match: model_type = match.group(1) - timestamp_str = match.group( - 2 - ) # Group 2 captures the timestamp + timestamp_str = match.group(2) # Group 2 captures the timestamp try: model_time = datetime.strptime( timestamp_str, "%Y_%m_%d_%H_%M_%S" ) - valid_models.append( - ( - model_time, - priority.get(model_type, 3), - model_file_path, - ) - ) + valid_models.append((model_time, priority.get(model_type, 3), model_file_path)) except ValueError: continue # Ignore files with malformed timestamps @@ -143,9 +99,6 @@ def load_model(self): rf"未找到训练好的模型文件 {self.model_path},请先训练模型" ) - # 兼容项目重构前保存的整模型 pickle 路径 - _register_legacy_model_aliases() - try: model = torch.load( self.model_path, @@ -153,7 +106,9 @@ def load_model(self): weights_only=False, ) except TypeError: # 如果旧版本 PyTorch 不认识 weights_only - model = torch.load(self.model_path, map_location=self.device) + model = torch.load( + self.model_path, map_location=self.device + ) model.eval() self.model = model.to(self.device) @@ -162,20 +117,16 @@ def load_model(self): if "missing keys" in str(e): error_msg += "\n可能是模型结构不匹配,请重新训练模型" raise e # 无法继续运行,退出程序 - - def export_onnx(self, outputpath, monster_count=MONSTER_COUNT): + + def export_onnx(self,outputpath, monster_count=MONSTER_COUNT): # 确保模型在 CPU 上(避免设备不一致) self.model = self.model.cpu() self.model.eval() # 生成虚拟输入(与模型同设备) device = next(self.model.parameters()).device - dummy_left_counts = torch.randint( - 0, 10, (1, monster_count), dtype=torch.int16, device=device - ) - dummy_right_counts = torch.randint( - 0, 10, (1, monster_count), dtype=torch.int16, device=device - ) + dummy_left_counts = torch.randint(0, 10, (1, monster_count), dtype=torch.int16, device=device) + dummy_right_counts = torch.randint(0, 10, (1, monster_count), dtype=torch.int16, device=device) # 获取符号和绝对值张量(确保在相同设备) left_signs = torch.sign(dummy_left_counts.to(torch.int64)).to(device) @@ -184,14 +135,9 @@ def export_onnx(self, outputpath, monster_count=MONSTER_COUNT): right_counts = torch.abs(dummy_right_counts.to(torch.int64)).to(device) # 导出参数 - input_names = [ - "left_signs", - "left_counts", - "right_signs", - "right_counts", - ] - dynamic_axes = {name: {0: "batch_size"} for name in input_names} - dynamic_axes["output"] = {0: "batch_size"} + input_names = ["left_signs", "left_counts", "right_signs", "right_counts"] + dynamic_axes = {name: {0: 'batch_size'} for name in input_names} + dynamic_axes["output"] = {0: 'batch_size'} # 导出 ONNX torch.onnx.export( @@ -202,14 +148,10 @@ def export_onnx(self, outputpath, monster_count=MONSTER_COUNT): output_names=["output"], dynamic_axes=dynamic_axes, opset_version=20, - verbose=True, # 开启详细输出便于调试 + verbose=True # 开启详细输出便于调试 ) - def get_prediction( - self, - left_counts: np.typing.ArrayLike, - right_counts: np.typing.ArrayLike, - ): + def get_prediction(self, left_counts: np.typing.ArrayLike, right_counts: np.typing.ArrayLike): if self.model is None: raise RuntimeError("模型未正确初始化") @@ -259,34 +201,20 @@ def get_prediction_with_terrain(self, full_features: np.typing.ArrayLike): raise RuntimeError("模型未正确初始化") # 检查特征向量长度 - expected_length = ( - MONSTER_COUNT * 2 + FIELD_FEATURE_COUNT * 2 - ) # 77L + 6L + 77R + 6R = 166 + expected_length = MONSTER_COUNT * 2 + FIELD_FEATURE_COUNT * 2 # 77L + 6L + 77R + 6R = 166 if len(full_features) != expected_length: - logger.warning( - f"特征向量长度不匹配: 期望{expected_length}, 实际{len(full_features)}" - ) + logger.warning(f"特征向量长度不匹配: 期望{expected_length}, 实际{len(full_features)}") # 如果长度不匹配,回退到原始方法 left_counts = full_features[:MONSTER_COUNT] - right_counts = full_features[MONSTER_COUNT : MONSTER_COUNT * 2] + right_counts = full_features[MONSTER_COUNT:MONSTER_COUNT*2] return self.get_prediction(left_counts, right_counts) # 提取各个部分 left_monsters = full_features[:MONSTER_COUNT] # 1L-77L - left_terrain = full_features[ - MONSTER_COUNT : MONSTER_COUNT + FIELD_FEATURE_COUNT - ] # 78L-83L - right_monsters = full_features[ - MONSTER_COUNT - + FIELD_FEATURE_COUNT : MONSTER_COUNT * 2 - + FIELD_FEATURE_COUNT - ] # 1R-77R - right_terrain = full_features[ - MONSTER_COUNT * 2 - + FIELD_FEATURE_COUNT : MONSTER_COUNT * 2 - + FIELD_FEATURE_COUNT * 2 - ] # 78R-83R - + left_terrain = full_features[MONSTER_COUNT:MONSTER_COUNT+FIELD_FEATURE_COUNT] # 78L-83L + right_monsters = full_features[MONSTER_COUNT+FIELD_FEATURE_COUNT:MONSTER_COUNT*2+FIELD_FEATURE_COUNT] # 1R-77R + right_terrain = full_features[MONSTER_COUNT*2+FIELD_FEATURE_COUNT:MONSTER_COUNT*2+FIELD_FEATURE_COUNT*2] # 78R-83R + # 合并怪物特征和地形特征(按照训练时的格式) left_counts = np.concatenate([left_monsters, left_terrain]) right_counts = np.concatenate([right_monsters, right_terrain]) @@ -294,62 +222,27 @@ def get_prediction_with_terrain(self, full_features: np.typing.ArrayLike): # 转换为张量并处理符号和绝对值 # 对于怪物特征,使用符号和绝对值 # 对于地形特征,不需要符号处理(地形特征本身就是0/1值) - left_monster_signs = torch.sign( - torch.tensor(left_monsters, dtype=torch.int16) - ) - left_terrain_signs = torch.ones_like( - torch.tensor(left_terrain, dtype=torch.int16) - ) # 地形特征符号为1 - left_signs = ( - torch.cat([left_monster_signs, left_terrain_signs]) - .unsqueeze(0) - .to(self.device) - ) - - left_monster_counts = torch.abs( - torch.tensor(left_monsters, dtype=torch.int16) - ) - left_terrain_counts = torch.tensor( - left_terrain, dtype=torch.int16 - ) # 地形特征直接使用原值 - left_counts_tensor = ( - torch.cat([left_monster_counts, left_terrain_counts]) - .unsqueeze(0) - .to(self.device) - ) - - right_monster_signs = torch.sign( - torch.tensor(right_monsters, dtype=torch.int16) - ) - right_terrain_signs = torch.ones_like( - torch.tensor(right_terrain, dtype=torch.int16) - ) # 地形特征符号为1 - right_signs = ( - torch.cat([right_monster_signs, right_terrain_signs]) - .unsqueeze(0) - .to(self.device) - ) - - right_monster_counts = torch.abs( - torch.tensor(right_monsters, dtype=torch.int16) - ) - right_terrain_counts = torch.tensor( - right_terrain, dtype=torch.int16 - ) # 地形特征直接使用原值 - right_counts_tensor = ( - torch.cat([right_monster_counts, right_terrain_counts]) - .unsqueeze(0) - .to(self.device) - ) + left_monster_signs = torch.sign(torch.tensor(left_monsters, dtype=torch.int16)) + left_terrain_signs = torch.ones_like(torch.tensor(left_terrain, dtype=torch.int16)) # 地形特征符号为1 + left_signs = torch.cat([left_monster_signs, left_terrain_signs]).unsqueeze(0).to(self.device) + + left_monster_counts = torch.abs(torch.tensor(left_monsters, dtype=torch.int16)) + left_terrain_counts = torch.tensor(left_terrain, dtype=torch.int16) # 地形特征直接使用原值 + left_counts_tensor = torch.cat([left_monster_counts, left_terrain_counts]).unsqueeze(0).to(self.device) + + right_monster_signs = torch.sign(torch.tensor(right_monsters, dtype=torch.int16)) + right_terrain_signs = torch.ones_like(torch.tensor(right_terrain, dtype=torch.int16)) # 地形特征符号为1 + right_signs = torch.cat([right_monster_signs, right_terrain_signs]).unsqueeze(0).to(self.device) + + right_monster_counts = torch.abs(torch.tensor(right_monsters, dtype=torch.int16)) + right_terrain_counts = torch.tensor(right_terrain, dtype=torch.int16) # 地形特征直接使用原值 + right_counts_tensor = torch.cat([right_monster_counts, right_terrain_counts]).unsqueeze(0).to(self.device) # 预测流程 with torch.no_grad(): # 使用修改后的模型前向传播流程,现在包含地形特征 prediction = self.model( - left_signs, - left_counts_tensor, - right_signs, - right_counts_tensor, + left_signs, left_counts_tensor, right_signs, right_counts_tensor ).item() # 确保预测值在有效范围内 diff --git a/src/models/predict_onnx.py b/predict_onnx.py similarity index 74% rename from src/models/predict_onnx.py rename to predict_onnx.py index 6ae999d..31a527f 100644 --- a/src/models/predict_onnx.py +++ b/predict_onnx.py @@ -5,12 +5,11 @@ import numpy as np import logging -from src.core.config import MONSTER_COUNT -from src.core.config import FIELD_FEATURE_COUNT +from config import MONSTER_COUNT +from config import FIELD_FEATURE_COUNT logger = logging.getLogger(__name__) - class CannotModel: def __init__(self, model_path="models"): self.model_path = self._resolve_model_path(model_path) @@ -36,7 +35,7 @@ def _resolve_model_path(self, path): if default_path.exists(): logger.info(f"Found default model: {default_path}") return str(default_path) - + logger.error(f"No valid ONNX model files found in {path}") return str(default_path) @@ -51,42 +50,36 @@ def load_model(self): """加载 ONNX 模型""" try: if not os.path.exists(self.model_path): - raise FileNotFoundError( - f"未找到 ONNX 模型文件 {self.model_path}" - ) - + raise FileNotFoundError(f"未找到 ONNX 模型文件 {self.model_path}") + # 配置会话选项 sess_options = ort.SessionOptions() - sess_options.graph_optimization_level = ( - ort.GraphOptimizationLevel.ORT_ENABLE_ALL - ) - + sess_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL + # 创建会话(默认使用 CPU) self.session = ort.InferenceSession( self.model_path, sess_options, - providers=["CPUExecutionProvider"], + providers=['CPUExecutionProvider'] ) - + except Exception as e: raise RuntimeError(f"ONNX 模型加载失败: {str(e)}") - def get_prediction( - self, left_counts: np.ndarray, right_counts: np.ndarray - ): + def get_prediction(self, left_counts: np.ndarray, right_counts: np.ndarray): if self.session is None: raise RuntimeError("模型未正确初始化") - + def validate_input(arr): """验证并转换输入数据""" # 转换为 int64 类型 arr = arr.astype(np.int64) - + # 添加批次维度(如果输入是单样本) if arr.ndim == 1: arr = arr[np.newaxis, :] # shape: (1, 56) return arr - + # 处理符号和绝对值,以匹配导出的模型输入 left_signs_arr = np.sign(left_counts).astype(np.int64) left_counts_arr = np.abs(left_counts).astype(np.int64) @@ -97,60 +90,47 @@ def validate_input(arr): "left_signs": validate_input(left_signs_arr), "left_counts": validate_input(left_counts_arr), "right_signs": validate_input(right_signs_arr), - "right_counts": validate_input(right_counts_arr), + "right_counts": validate_input(right_counts_arr) } - + # 执行推理 try: output = self.session.run( - output_names=["output"], input_feed=inputs + output_names=["output"], + input_feed=inputs ) # output 是一个列表,output[0] 是形状为 (batch_size, 1) 的数组 prediction = output[0].flatten()[0] except Exception as e: raise RuntimeError(f"推理失败: {str(e)}") - + # 后处理(与原逻辑一致) if np.isnan(prediction) or np.isinf(prediction): logger.warning("警告: 预测结果包含NaN或Inf,返回默认值0.5") prediction = 0.5 - + prediction = np.clip(prediction, 0.0, 1.0) return float(prediction) - + def get_prediction_with_terrain(self, full_features: np.ndarray): """使用包含地形特征的完整特征向量进行预测(ONNX版本)""" if self.session is None: raise RuntimeError("模型未正确初始化") # 检查特征向量长度 - expected_length = ( - MONSTER_COUNT * 2 + FIELD_FEATURE_COUNT * 2 - ) # 77L + 6L + 77R + 6R = 166 + expected_length = MONSTER_COUNT * 2 + FIELD_FEATURE_COUNT * 2 # 77L + 6L + 77R + 6R = 166 if len(full_features) != expected_length: - logger.warning( - f"特征向量长度不匹配: 期望{expected_length}, 实际{len(full_features)}" - ) + logger.warning(f"特征向量长度不匹配: 期望{expected_length}, 实际{len(full_features)}") # 如果长度不匹配,回退到原始方法 left_counts = full_features[:MONSTER_COUNT] - right_counts = full_features[MONSTER_COUNT : MONSTER_COUNT * 2] + right_counts = full_features[MONSTER_COUNT:MONSTER_COUNT*2] return self.get_prediction(left_counts, right_counts) # 提取各个部分 left_monsters = full_features[:MONSTER_COUNT] # 1L-77L - left_terrain = full_features[ - MONSTER_COUNT : MONSTER_COUNT + FIELD_FEATURE_COUNT - ] # 78L-83L - right_monsters = full_features[ - MONSTER_COUNT - + FIELD_FEATURE_COUNT : MONSTER_COUNT * 2 - + FIELD_FEATURE_COUNT - ] # 1R-77R - right_terrain = full_features[ - MONSTER_COUNT * 2 - + FIELD_FEATURE_COUNT : MONSTER_COUNT * 2 - + FIELD_FEATURE_COUNT * 2 - ] # 78R-83R + left_terrain = full_features[MONSTER_COUNT:MONSTER_COUNT+FIELD_FEATURE_COUNT] # 78L-83L + right_monsters = full_features[MONSTER_COUNT+FIELD_FEATURE_COUNT:MONSTER_COUNT*2+FIELD_FEATURE_COUNT] # 1R-77R + right_terrain = full_features[MONSTER_COUNT*2+FIELD_FEATURE_COUNT:MONSTER_COUNT*2+FIELD_FEATURE_COUNT*2] # 78R-83R # 处理左侧特征 left_monster_signs = np.sign(left_monsters).astype(np.int64) @@ -158,21 +138,15 @@ def get_prediction_with_terrain(self, full_features: np.ndarray): left_signs = np.concatenate([left_monster_signs, left_terrain_signs]) left_monster_counts = np.abs(left_monsters).astype(np.int64) - left_counts = np.concatenate( - [left_monster_counts, left_terrain.astype(np.int64)] - ) + left_counts = np.concatenate([left_monster_counts, left_terrain.astype(np.int64)]) # 处理右侧特征 right_monster_signs = np.sign(right_monsters).astype(np.int64) right_terrain_signs = np.ones_like(right_terrain).astype(np.int64) - right_signs = np.concatenate( - [right_monster_signs, right_terrain_signs] - ) + right_signs = np.concatenate([right_monster_signs, right_terrain_signs]) right_monster_counts = np.abs(right_monsters).astype(np.int64) - right_counts = np.concatenate( - [right_monster_counts, right_terrain.astype(np.int64)] - ) + right_counts = np.concatenate([right_monster_counts, right_terrain.astype(np.int64)]) def validate_input(arr): """验证并转换输入数据""" @@ -185,13 +159,14 @@ def validate_input(arr): "left_signs": validate_input(left_signs), "left_counts": validate_input(left_counts), "right_signs": validate_input(right_signs), - "right_counts": validate_input(right_counts), + "right_counts": validate_input(right_counts) } # 执行推理 try: output = self.session.run( - output_names=["output"], input_feed=inputs + output_names=["output"], + input_feed=inputs ) # output[0] 是形状为 (batch_size, 1) 的数组 prediction = output[0].flatten()[0] @@ -204,4 +179,4 @@ def validate_input(arr): prediction = 0.5 prediction = np.clip(prediction, 0.0, 1.0) - return float(prediction) + return float(prediction) \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index f7776a7..286692d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "CannotMax-Greenvine" -version = "2.0.0" +version = "1.0.8" description = "这是一个基于深度学习的明日方舟游戏辅助工具,用于自动识别游戏画面中的单位并预测战斗结果。" readme = "README.md" requires-python = ">=3.10" @@ -21,7 +21,7 @@ dependencies = [ ] [dependency-groups] -dev = ["onnx>=1.19.0", "onnxscript>=0.7.0", "types-toml", "pandas-stubs", "pyinstaller>=6.15.0"] +dev = ["onnx>=1.19.0", "onnxscript>=0.7.0", "pyinstaller>=6.15.0"] [project.optional-dependencies] cpu = ["torch", "torchvision"] diff --git a/src/recognition/recognize.py b/recognize.py similarity index 81% rename from src/recognition/recognize.py rename to recognize.py index d021439..72ae417 100644 --- a/src/recognition/recognize.py +++ b/recognize.py @@ -5,16 +5,15 @@ from PIL import ImageGrab from rapidocr import RapidOCR, EngineType -from src.core.config import MONSTER_DATA, MONSTER_IMAGES, MONSTER_COUNT -from src.core.paths import ensure_tmp_images_dir, image_path -from . import find_monster_zone -from src.game.winrt_capture import WinRTScreenCapture +from config import MONSTER_DATA, MONSTER_IMAGES, MONSTER_COUNT +import find_monster_zone +from winrt_capture import WinRTScreenCapture logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) # 是否启用debug模式 -INTELLIGENT_WORKERS_DEBUG = True +intelligent_workers_debug = True # 数字区域相对坐标 relative_regions_nums = [ @@ -35,7 +34,6 @@ (0.8700, 0.05, 1.0000, 0.80), ] - def get_rapidocr_engine(prefer_gpu=False): """ prefer_gpu (bool): 是否优先尝试使用GPU @@ -43,7 +41,6 @@ def get_rapidocr_engine(prefer_gpu=False): try: if prefer_gpu: import torch - if torch.cuda.is_available(): return RapidOCR( params={ @@ -59,21 +56,11 @@ def get_rapidocr_engine(prefer_gpu=False): # 如果没有GPU可用,使用CPU onnxruntime return RapidOCR() - class RecognizeMonster: - ROI_RELATIVE = [ - (0.2464, 0.8410), - (0.7542, 0.9510), - ] # 16:9下怪物区域相对坐标 - - def __init__( - self, - method: str = "ADB", - window_name: str | None = None, - monitor_index: int | None = None, - ): + ROI_RELATIVE = [(0.2464, 0.8410), (0.7542, 0.9510)] # 16:9下怪物区域相对坐标 + def __init__(self, method: str = "ADB", window_name: str | None = None, monitor_index: int | None = None): self.method = method - self.main_roi = [(0, 0), (1919, 1079)] # 主区域坐标 + self.main_roi = [(0, 0), (1919, 1079)] # 主区域坐标 # 鼠标交互全局变量 self.roi_box = [] self.drawing = False @@ -100,12 +87,12 @@ def __init__( self.main_roi = [(0, 0), (w - 1, h - 1)] except Exception as e: logger.exception("WinRT capture init failed: %s", e) - self._winrt = None # 将 _winrt 设置为 None,表示初始化失败 - raise # 重新抛出异常,以便上层捕获 + self._winrt = None # 将 _winrt 设置为 None,表示初始化失败 + raise # 重新抛出异常,以便上层捕获 else: logger.info("WIN 模式未指定窗口或显示器,将使用 PIL 作为回退") - def mouse_callback(self, event, x: int, y: int, flags, param): + def mouse_callback(self, event, x:int, y:int, flags, param): if event == cv2.EVENT_LBUTTONDOWN: self.roi_box = [(x, y)] self.drawing = True @@ -135,15 +122,8 @@ def select_roi(self): return None # 添加操作提示 - cv2.putText( - img, - "Drag to select area | ENTER:confirm | ESC:retry", - (10, 30), - cv2.FONT_HERSHEY_SIMPLEX, - 0.7, - (0, 0, 255), - 2, - ) + cv2.putText(img, "Drag to select area | ENTER:confirm | ESC:retry", + (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) # 显示窗口 cv2.namedWindow("Select ROI", cv2.WINDOW_NORMAL) @@ -152,7 +132,7 @@ def select_roi(self): cv2.imshow("Select ROI", img) # 添加示例图片(要后弹出才看得见) - example_img = cv2.imread(str(image_path("eg"))) + example_img = cv2.imread("images/eg.png") # 显示示例图片在单独的窗口中 cv2.imshow("example", example_img) @@ -172,9 +152,7 @@ def select_roi(self): self.roi_box = [] continue - def find_best_match( - target: cv2.typing.MatLike, ref_images: dict[int, cv2.typing.MatLike] - ): + def find_best_match(target: cv2.typing.MatLike, ref_images: dict[int, cv2.typing.MatLike]): """ 模板匹配找到最佳匹配的参考图像 :param target: 目标图像 @@ -213,7 +191,7 @@ def get_manual_screenshot(self) -> cv2.typing.MatLike: screenshot = cv2.cvtColor(screenshot, cv2.COLOR_RGB2BGR) try: # 手动框选的截图需先识别目标区域 - cv2.imwrite(str(ensure_tmp_images_dir() / "zone1.png"), screenshot) + cv2.imwrite(f"images/tmp/zone1.png", screenshot) d_avatar, d_nums = find_monster_zone.cutFrame(screenshot) height, width, _ = screenshot.shape divisors = np.array([width, height, width, height]) @@ -228,10 +206,7 @@ def get_manual_screenshot(self) -> cv2.typing.MatLike: y_min = max(0, y_min) # 假如找到过能用main_roi的就存起来 logger.info(f"识别到目标区域:{[(x_min, y_min), (x_max, y_max)]}") - self.main_roi = [ - (x1 + x_min, y1 + y_min), - (x1 + x_max, y1 + y_max), - ] + self.main_roi = [(x1 + x_min, y1 + y_min), (x1 + x_max, y1 + y_max)] screenshot = screenshot[y_min:y_max, x_min:x_max] logger.info(f"区域更新为: {self.main_roi}") except Exception as e: @@ -282,9 +257,9 @@ def process_regions( main_height = screenshot.shape[0] main_width = screenshot.shape[1] - if INTELLIGENT_WORKERS_DEBUG: # 如果处于debug模式 + if intelligent_workers_debug: # 如果处于debug模式 # 存储模板图像用于debug - cv2.imwrite(str(ensure_tmp_images_dir() / "zone.png"), screenshot) + cv2.imwrite(f"images/tmp/zone.png", screenshot) # 遍历所有区域 for idx, rel in enumerate(relative_regions): @@ -299,23 +274,14 @@ def process_regions( sub_roi = screenshot[ry1:ry2, rx1:rx2] # 图像匹配 - matched_id, confidence = find_best_match( - sub_roi, self.ref_images - ) - logger.info( - f"target: {idx} matched_id: {matched_id}, confidence: {confidence:.4f}" - ) + matched_id, confidence = find_best_match(sub_roi, self.ref_images) + logger.info(f"target: {idx} matched_id: {matched_id}, confidence: {confidence:.4f}") if matched_id != 0 and confidence < matched_threshold: raise ValueError(f"模板匹配置信度过低: {confidence}") except Exception as e: logger.exception(f"区域 {idx} 匹配失败: {str(e)}") results.append( - { - "region_id": idx, - "matched_id": matched_id, - "number": "N/A", - "error": str(e), - } + {"region_id": idx, "matched_id": matched_id, "number": "N/A", "error": str(e)} ) continue try: @@ -329,30 +295,20 @@ def process_regions( # 提取OCR识别用的子区域 sub_roi_num = screenshot[ry1_num:ry2_num, rx1_num:rx2_num] processed = preprocess(sub_roi_num) # 二值化预处理 - processed = crop_to_min_bounding_rect( - processed - ) # 去除多余黑框 - processed = add_black_border( - processed, border_size=3 - ) # 加上3像素黑框 + processed = crop_to_min_bounding_rect(processed) # 去除多余黑框 + processed = add_black_border(processed, border_size=3) # 加上3像素黑框 # OCR识别(保留优化后的处理逻辑) number, ocr_confidence = self.do_num_ocr(processed) if number != "" and ocr_confidence < ocr_threshold: raise ValueError(f"OCR置信度过低: {ocr_confidence}") - if INTELLIGENT_WORKERS_DEBUG: # 如果处于debug模式 + if intelligent_workers_debug: # 如果处于debug模式 # 存储模板图像用于debug - cv2.imwrite( - str(ensure_tmp_images_dir() / f"target_{idx}.png"), - sub_roi, - ) + cv2.imwrite(f"images/tmp/target_{idx}.png", sub_roi) # 存储OCR图像用于debug - cv2.imwrite( - str(ensure_tmp_images_dir() / f"number_{idx}.png"), - processed, - ) + cv2.imwrite(f"images/tmp/number_{idx}.png", processed) if number == "" and matched_id != 0: raise ValueError("发现有怪物但无数量异常数据!") @@ -370,22 +326,13 @@ def process_regions( except Exception as e: logger.exception(f"区域 {idx} OCR识别失败: {str(e)}") results.append( - { - "region_id": idx, - "matched_id": matched_id, - "number": "N/A", - "error": str(e), - } + {"region_id": idx, "matched_id": matched_id, "number": "N/A", "error": str(e)} ) return results - + def do_num_ocr(self, img: cv2.typing.MatLike): - result = self.rapidocr_eng( - img, use_det=False, use_cls=False, use_rec=True - ) - logger.info( - f"OCR: text: '{result.txts[0]}', score: {result.scores[0]}" - ) + result = self.rapidocr_eng(img, use_det=False, use_cls=False, use_rec=True) + logger.info(f"OCR: text: '{result.txts[0]}', score: {result.scores[0]}") if result.txts[0] != "" and not result.txts[0].isdigit(): raise ValueError(f"OCR识别结果不是数字: '{result.txts[0]}'") return result.txts[0], result.scores[0] @@ -411,9 +358,7 @@ def crop_to_min_bounding_rect(image: cv2.typing.MatLike): else: gray = image # 寻找轮廓 - contours, _ = cv2.findContours( - gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE - ) + contours, _ = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 如果没有找到轮廓就直接返回原图 if not contours: return image @@ -450,9 +395,7 @@ def preprocess(img: cv2.typing.MatLike): closed = bright_mask # 去除细小噪声:过滤不够大的连通区域 - contours, _ = cv2.findContours( - closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE - ) + contours, _ = cv2.findContours(closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for contour in contours: x, y, w, h = cv2.boundingRect(contour) if w <= 1: @@ -465,9 +408,7 @@ def preprocess(img: cv2.typing.MatLike): return closed -def find_best_match( - target: cv2.typing.MatLike, ref_images: dict[int, cv2.typing.MatLike] -): +def find_best_match(target: cv2.typing.MatLike, ref_images: dict[int, cv2.typing.MatLike]): """ 模板匹配找到最佳匹配的参考图像 :param target: 目标图像 @@ -503,35 +444,30 @@ def load_ref_images(ref_dir="images"): for i in range(MONSTER_COUNT + 1): # path = os.path.join(ref_dir, f"{i}.png") # if os.path.exists(path): - # img = cv2.imread(path, cv2.IMREAD_COLOR_BGR) + # img = cv2.imread(path, cv2.IMREAD_COLOR_BGR) if i == 0: img = MONSTER_IMAGES.get("empty") else: img = MONSTER_IMAGES.get(MONSTER_DATA["原始名称"][i]) if img is None: - logger.error( - f"无法加载参考图片 i={i}, 名称={MONSTER_DATA['原始名称'][i] if i > 0 else 'empty'}" - ) + logger.error(f"无法加载参考图片 i={i}, 名称={MONSTER_DATA['原始名称'][i] if i > 0 else 'empty'}") continue # 裁切模板匹配图像比例 img_crop = img[ - int(img.shape[0] * 0.16) : int( - img.shape[0] * 0.80 - ), # 高度取靠上部分 - int(img.shape[1] * 0.18) : int( - img.shape[1] * 0.82 - ), # 宽度与高度一致 + int(img.shape[0] * 0.16) : int(img.shape[0] * 0.80), # 高度取靠上部分 + int(img.shape[1] * 0.18) : int(img.shape[1] * 0.82), # 宽度与高度一致 ] # 调整参考图像大小以匹配目标图像 ref_resized = cv2.resize(img_crop, (74, 74)) ref_resized = ref_resized[0:70, :] - if INTELLIGENT_WORKERS_DEBUG: # 如果处于debug模式 + if intelligent_workers_debug: # 如果处于debug模式 # 存储模板图像用于debug - tmp_dir = ensure_tmp_images_dir() - cv2.imwrite(str(tmp_dir / f"xref_{i}.png"), ref_resized) + if not os.path.exists("images/tmp"): + os.makedirs("images/tmp") + cv2.imwrite(f"images/tmp/xref_{i}.png", ref_resized) ref_images[i] = ref_resized return ref_images diff --git a/src/analysis/sim_mc.py b/sim_mc.py similarity index 67% rename from src/analysis/sim_mc.py rename to sim_mc.py index c1efc7c..df80f9e 100644 --- a/src/analysis/sim_mc.py +++ b/sim_mc.py @@ -1,33 +1,25 @@ -from collections import Counter +from collections import Counter, defaultdict from concurrent.futures import ThreadPoolExecutor -from pathlib import Path import queue import threading +import time import os import copy from enum import Enum, auto import tkinter as tk - # from tkinter import messagebox # messagebox 已被自定义提示替代,可以注释或移除 from PIL import Image, ImageTk - -import json -import random -import sys - - - -if __name__ == "__main__": - sys.path.insert(0, str(Path(__file__).parent.parent.parent)) - from src.simulation.battle_field import Battlefield - from src.core.config import MONSTER_DATA, MONSTER_IMAGES, MONSTER_COUNT - from src.core.paths import DATA_DIR, PROJECT_ROOT, simulation_path - from src.simulation.utils import Faction -else: - from ..simulation.battle_field import Battlefield - from ..core.config import MONSTER_DATA, MONSTER_IMAGES, MONSTER_COUNT - from ..core.paths import DATA_DIR, PROJECT_ROOT, simulation_path - from ..simulation.utils import Faction +import math +from simulator.battle_field import Battlefield # 确保 Battlefield 已导入 +from simulator.monsters import MonsterFactory # 确保 MonsterFactory 已导入 +from simulator.utils import MONSTER_MAPPING, REVERSE_MONSTER_MAPPING, Faction # 根据你的实际路径调整 +from simulator.vector2d import FastVector # 确保 FastVector 已导入 +from unit import Unit # 确保 Unit 已导入 +import json # REMOVED_TEAM_INTERFACE: Added missing import for the main block +import random # REMOVED_TEAM_INTERFACE: Added missing import for the main block +import sys # Import sys for stdin +from simulator.monsters import AttackState, Monster, MonsterFactory +from recognize import MONSTER_COUNT class AppState(Enum): @@ -46,19 +38,11 @@ def __init__(self, ui_update_callback): def transition_to(self, new_state): """状态转换并触发UI更新""" allowed_transitions = { - AppState.INITIAL: [ - AppState.INITIAL, - AppState.SETUP, - AppState.ENDED, - ], - AppState.SETUP: [ - AppState.INITIAL, - AppState.SETUP, - AppState.SIMULATING, - ], + AppState.INITIAL: [AppState.INITIAL, AppState.SETUP, AppState.ENDED], + AppState.SETUP: [AppState.INITIAL, AppState.SETUP, AppState.SIMULATING], AppState.SIMULATING: [AppState.PAUSED, AppState.ENDED], AppState.PAUSED: [AppState.SIMULATING, AppState.SETUP], - AppState.ENDED: [AppState.INITIAL, AppState.SETUP], + AppState.ENDED: [AppState.INITIAL, AppState.SETUP] } if new_state in allowed_transitions[self.state]: @@ -70,55 +54,47 @@ def transition_to(self, new_state): def get_control_states(self): """返回各控件的状态字典""" states = { - "deploy": {"state": tk.NORMAL, "text": "部署怪物"}, - "confirm_start": {"state": tk.DISABLED}, - "pause": {"state": tk.DISABLED, "text": "暂停"}, - "restore": {"state": tk.DISABLED}, - "speed_entry": {"state": tk.NORMAL}, - "clear": {"state": tk.NORMAL}, - "timer": {"text": ""}, + 'deploy': {'state': tk.NORMAL, 'text': '部署怪物'}, + 'confirm_start': {'state': tk.DISABLED}, + 'pause': {'state': tk.DISABLED, 'text': '暂停'}, + 'restore': {'state': tk.DISABLED}, + 'speed_entry': {'state': tk.NORMAL}, + 'clear': {'state': tk.NORMAL}, + 'timer': {'text': ''} } if self.state == AppState.INITIAL: pass elif self.state == AppState.SETUP: - states.update( - { - "deploy": {"state": tk.NORMAL, "text": "重新部署"}, - "confirm_start": {"state": tk.NORMAL}, - "timer": {"text": "未开始"}, - } - ) + states.update({ + 'deploy': {'state': tk.NORMAL, 'text': '重新部署'}, + 'confirm_start': {'state': tk.NORMAL}, + 'timer': {'text': '未开始'} + }) elif self.state == AppState.SIMULATING: - states.update( - { - "deploy": {"state": tk.DISABLED, "text": "重新部署"}, - "confirm_start": {"state": tk.DISABLED}, - "pause": {"state": tk.NORMAL, "text": "暂停"}, - "clear": {"state": tk.DISABLED}, - } - ) + states.update({ + 'deploy': {'state': tk.DISABLED, 'text': '重新部署'}, + 'confirm_start': {'state': tk.DISABLED}, + 'pause': {'state': tk.NORMAL, 'text': '暂停'}, + 'clear': {'state': tk.DISABLED}, + }) elif self.state == AppState.PAUSED: - states.update( - { - "restore": {"state": tk.NORMAL}, - "pause": {"state": tk.NORMAL, "text": "继续"}, - "deploy": {"state": tk.NORMAL, "text": "重新部署"}, - } - ) + states.update({ + 'restore': {'state': tk.NORMAL}, + 'pause': {'state': tk.NORMAL, 'text': '继续'}, + 'deploy': {'state': tk.NORMAL, 'text': '重新部署'} + }) elif self.state == AppState.ENDED: - states.update( - { - "deploy": {"state": tk.DISABLED, "text": "重新部署"}, - "restore": {"state": tk.NORMAL}, - "pause": {"state": tk.DISABLED, "text": "暂停"}, - "timer": {"text": "战斗结束"}, - } - ) + states.update({ + 'deploy': {'state': tk.DISABLED, 'text': '重新部署'}, + 'restore': {'state': tk.NORMAL}, + 'pause': {'state': tk.DISABLED, 'text': '暂停'}, + 'timer': {'text': '战斗结束'} + }) return states @@ -169,16 +145,6 @@ def __init__(self, master: tk.Tk, battle_data): self.state_machine.transition_to(AppState.INITIAL) self.enter_setup_phase() - def _resolve_monster_icon_key(self, monster_name: str) -> str: - """根据怪物名称解析图标文件名。""" - if monster_name in self.monster_icon_key_map: - return self.monster_icon_key_map[monster_name] - - if monster_name in MONSTER_IMAGES: - return monster_name - - return self.default_icon_key - def update_ui_state(self): """根据当前状态更新所有控件状态""" states = self.state_machine.get_control_states() @@ -191,10 +157,8 @@ def hide_window(self): def load_assets(self): self.icons = {} - self.monster_icon_key_map = {} - self.default_icon_key = "empty" if "empty" in MONSTER_IMAGES else "" try: - with open(simulation_path("monsters.json"), encoding="utf-8") as f: + with open("simulator/monsters.json", encoding='utf-8') as f: self.monster_data = json.load(f)["monsters"] except FileNotFoundError: print("错误: monsters.json 未找到,请检查路径!") @@ -202,47 +166,21 @@ def load_assets(self): return - # 建立“名称/原始名称 -> 图标文件名”的兼容映射。 - for _, row in MONSTER_DATA.iterrows(): - monster_name = str(row["名称"]) - original_name = str(row["原始名称"]) - - icon_key = None - if original_name in MONSTER_IMAGES: - icon_key = original_name - elif monster_name in MONSTER_IMAGES: - icon_key = monster_name - - if icon_key is None: - icon_key = self.default_icon_key - - self.monster_icon_key_map[monster_name] = icon_key - self.monster_icon_key_map[original_name] = icon_key - - # 只为实际存在的图片创建 Tk 图像,避免访问旧的数字编号路径。 - for image_key, image_array in MONSTER_IMAGES.items(): + for i in range(self.num_monsters): + image_file_id = i + 1 try: - if image_array is None: - raise ValueError("图像数据为空") - - image = Image.fromarray(image_array[:, :, ::-1]) - image_40 = image.resize((40, 40)) - self.icons[image_key] = { - "red": ImageTk.PhotoImage(image_40, master=self.master), - "blue": ImageTk.PhotoImage( - image_40.transpose(Image.FLIP_LEFT_RIGHT), - master=self.master, - ), + image = Image.open(f'images/{image_file_id}.png') + self.icons[i] = { + "red": ImageTk.PhotoImage(image.resize((40, 40))), + "blue": ImageTk.PhotoImage(image.resize((40, 40)).transpose(Image.FLIP_LEFT_RIGHT)) } except Exception as e: - print(f"加载图标错误 (图标键: {image_key}): {str(e)}") - - if self.default_icon_key and self.default_icon_key not in self.icons: - fallback = Image.new("RGB", (40, 40), "gray") - self.icons[self.default_icon_key] = { - "red": ImageTk.PhotoImage(fallback, master=self.master), - "blue": ImageTk.PhotoImage(fallback, master=self.master), - } + # 同样,show_message_below_button 可能还不可用 + print(f"加载图标错误 (图标键: {i}, 文件名ID: {image_file_id}): {str(e)}") + self.icons[i] = { + "red": ImageTk.PhotoImage(Image.new("RGB", (40, 40), "gray")), + "blue": ImageTk.PhotoImage(Image.new("RGB", (40, 40), "gray")) + } def init_battlefield_for_setup(self): self.state_machine.transition_to(AppState.SETUP) @@ -253,8 +191,7 @@ def init_battlefield_for_setup(self): right_army_config = self.battle_data.get("right", {}) self.battle_field.setup_battle( - left_army_config, right_army_config, self.monster_data - ) + left_army_config, right_army_config, self.monster_data) while self.battle_field.gameTime < 6.0: result = self.battle_field.run_one_frame() if result: @@ -262,8 +199,7 @@ def init_battlefield_for_setup(self): def _normalize_outcome(self, outcome): try: - from src.simulation.utils import Faction - + from simulator.utils import Faction if outcome == Faction.LEFT: return "LEFT" if outcome == Faction.RIGHT: @@ -286,9 +222,7 @@ def create_widgets(self): self.message_label.pack(pady=2, anchor="w") # ====== 蒙特卡洛控制区 ====== - mc_frame = tk.LabelFrame( - self.master, text="蒙特卡洛模拟", padx=10, pady=8 - ) + mc_frame = tk.LabelFrame(self.master, text="蒙特卡洛模拟", padx=10, pady=8) mc_frame.pack(fill=tk.X, padx=10, pady=8) tk.Label(mc_frame, text="模拟次数:").grid(row=0, column=0, sticky="w") @@ -298,47 +232,36 @@ def create_widgets(self): self.mc_seed_var = tk.StringVar(value="") # 可选随机种子 tk.Label(mc_frame, text="随机种子(可空):").grid( - row=0, column=2, sticky="w", padx=(20, 0) - ) + row=0, column=2, sticky="w", padx=(20, 0)) self.mc_seed_entry = tk.Entry( - mc_frame, width=12, textvariable=self.mc_seed_var - ) + mc_frame, width=12, textvariable=self.mc_seed_var) self.mc_seed_entry.grid(row=0, column=3, sticky="w", padx=6) self.mc_run_button = tk.Button( - mc_frame, text="运行蒙卡", command=self.start_monte_carlo_threads - ) + mc_frame, text="运行蒙卡", command=self.start_monte_carlo_threads) self.mc_run_button.grid(row=0, column=4, padx=12) self.mc_stop_button = tk.Button( - mc_frame, - text="停止", - state=tk.DISABLED, - command=self.stop_monte_carlo_threads, - ) + mc_frame, text="停止", state=tk.DISABLED, command=self.stop_monte_carlo_threads) self.mc_stop_button.grid(row=0, column=5, padx=4) # ... self.mc_canvas = tk.Canvas(mc_frame, height=160, bg="white") - self.mc_canvas.grid( - row=2, column=0, columnspan=6, sticky="we", pady=(6, 2) - ) + self.mc_canvas.grid(row=2, column=0, columnspan=6, + sticky="we", pady=(6, 2)) self.mc_canvas.bind( - "", lambda e: self._redraw_mc_canvas() - ) # 动态重绘 + "", lambda e: self._redraw_mc_canvas()) # 动态重绘 # 结果文字 self.mc_result_text = tk.Text(mc_frame, height=6, width=70) self.mc_result_text.grid( - row=1, column=0, columnspan=5, pady=(8, 4), sticky="we" - ) + row=1, column=0, columnspan=5, pady=(8, 4), sticky="we") self.mc_result_text.configure(state=tk.DISABLED) # 简单条形图画布(显示胜负/平局分布) self.mc_canvas = tk.Canvas(mc_frame, height=160, bg="white") - self.mc_canvas.grid( - row=2, column=0, columnspan=5, sticky="we", pady=(6, 2) - ) + self.mc_canvas.grid(row=2, column=0, columnspan=5, + sticky="we", pady=(6, 2)) self.canvas = None def run_single_battle(self, left_cfg, right_cfg): @@ -359,8 +282,7 @@ def run_single_battle(self, left_cfg, right_cfg): outcome = "DRAW" else: try: - from src.simulation.utils import Faction - + from simulator.utils import Faction if result == Faction.LEFT: outcome = "LEFT" elif result == Faction.RIGHT: @@ -404,17 +326,13 @@ def start_monte_carlo_threads(self): try: seed_val = int(seed_txt) except ValueError: - self.show_message_below_button( - "随机种子需为整数或留空", is_error=True - ) + self.show_message_below_button("随机种子需为整数或留空", is_error=True) return - left_cfg = copy.deepcopy(self.battle_data.get("left", {})) + left_cfg = copy.deepcopy(self.battle_data.get("left", {})) right_cfg = copy.deepcopy(self.battle_data.get("right", {})) if not left_cfg or not right_cfg: - self.show_message_below_button( - "错误:左右双方任一为空,无法进行蒙卡", is_error=True - ) + self.show_message_below_button("错误:左右双方任一为空,无法进行蒙卡", is_error=True) return # 状态初始化 @@ -441,21 +359,16 @@ def start_monte_carlo_threads(self): # 关键:制造足够多的小任务(比如每个任务 10~50 次) # 这样就能频繁完成并推送结果,界面“按完成块”刷新 - TARGET_TASKS = min(max(32, workers * 16), runs) # 至少几十个分片 + TARGET_TASKS = min(max(32, workers * 16), runs) # 至少几十个分片 chunk = max(1, runs // TARGET_TASKS) remain = runs idx = 0 while remain > 0: n = min(chunk, remain) - fut = self.executor.submit( - self._mc_task, - n, - left_cfg, - right_cfg, - None if seed_val is None else seed_val + idx, - self.mc_stop_event, - ) + fut = self.executor.submit(self._mc_task, n, left_cfg, right_cfg, + None if seed_val is None else seed_val + idx, + self.mc_stop_event) # 回调里**不要**动 UI;只把结果丢进队列 def _on_done(f): @@ -538,13 +451,10 @@ def _mc_finish(self): self.show_message_below_button("蒙卡完成", is_error=False) else: self.show_message_below_button( - f"已停止(完成 {done}/{target} 次)", is_error=False - ) + f"已停止(完成 {done}/{target} 次)", is_error=False) self.mc_stop_event.clear() - def _render_mc_text_and_chart( - self, counts: Counter, done: int, target: int - ): + def _render_mc_text_and_chart(self, counts: Counter, done: int, target: int): left_win = counts.get("LEFT", 0) right_win = counts.get("RIGHT", 0) draw_cnt = counts.get("DRAW", 0) @@ -566,11 +476,7 @@ def _render_mc_text_and_chart( self.mc_result_text.configure(state=tk.DISABLED) # 图 - items = [ - ("左方胜", left_win), - ("右方胜", right_win), - ("平局", draw_cnt), - ] + items = [("左方胜", left_win), ("右方胜", right_win), ("平局", draw_cnt)] self.draw_mc_barchart(items, total=max(1, done)) # 避免除零 def _redraw_mc_canvas(self): @@ -578,11 +484,9 @@ def _redraw_mc_canvas(self): with self.mc_lock: counts = Counter(self._mc_counts) done = self._mc_total_done - items = [ - ("左方胜", counts.get("LEFT", 0)), - ("右方胜", counts.get("RIGHT", 0)), - ("平局", counts.get("DRAW", 0)), - ] + items = [("左方胜", counts.get("LEFT", 0)), + ("右方胜", counts.get("RIGHT", 0)), + ("平局", counts.get("DRAW", 0))] self.draw_mc_barchart(items, total=max(1, done)) def draw_mc_barchart(self, items, total): @@ -599,52 +503,44 @@ def draw_mc_barchart(self, items, total): H = int(c.winfo_height()) or 160 pad_x = 40 pad_y = 24 - bar_space = W - 2 * pad_x + bar_space = (W - 2*pad_x) bar_w = bar_space // max(1, len(items)) - 16 max_cnt = max(x[1] for x in items) or 1 # 坐标轴 - c.create_line(pad_x, H - pad_y, W - pad_x // 2, H - pad_y) # x轴 - c.create_line(pad_x, pad_y // 2, pad_x, H - pad_y) # y轴 + c.create_line(pad_x, H - pad_y, W - pad_x//2, H - pad_y) # x轴 + c.create_line(pad_x, pad_y//2, pad_x, H - pad_y) # y轴 for i, (label, cnt) in enumerate(items): x0 = pad_x + i * (bar_w + 16) + 8 x1 = x0 + bar_w # 高度按计数线性映射 h_ratio = cnt / max_cnt if max_cnt > 0 else 0.0 - bar_h = int((H - 2 * pad_y) * h_ratio) + bar_h = int((H - 2*pad_y) * h_ratio) y0 = H - pad_y - bar_h y1 = H - pad_y # 绘制条 c.create_rectangle(x0, y0, x1, y1, fill="#6aa84f", outline="") # 文本:计数与百分比 pct = f"{(cnt/total):.1%}" - c.create_text( - (x0 + x1) // 2, y0 - 10, text=f"{cnt} ({pct})", anchor="s" - ) + c.create_text((x0+x1)//2, y0-10, text=f"{cnt} ({pct})", anchor="s") # 类别名 - c.create_text( - (x0 + x1) // 2, H - pad_y + 12, text=label, anchor="n" - ) + c.create_text((x0+x1)//2, H - pad_y + 12, text=label, anchor="n") def refresh_canvas_display(self): if self.monte_carlo_mode: return # ... 原本的画网格/单位/连线/计时 等逻辑保持不变 - def show_message_below_button( - self, message, is_error=False, duration=5000 - ): + def show_message_below_button(self, message, is_error=False, duration=5000): """在按钮下方显示文字提示,并在指定时间后消失""" if self.message_label: self.message_label.config( - text=message, fg="red" if is_error else "black" - ) + text=message, fg="red" if is_error else "black") if self.message_timer_id: self.master.after_cancel(self.message_timer_id) self.message_timer_id = self.master.after( - duration, self.clear_message_below_button - ) + duration, self.clear_message_below_button) def clear_message_below_button(self): """清除提示信息""" @@ -665,13 +561,10 @@ def main(): root = tk.Tk() # root.withdraw() # 如果不需要立即隐藏主窗口,可以注释掉 - initial_battle_setup = { - "left": {"炮击组长": 3, "“庞贝”": 2}, - "right": {"沸血骑士团精锐": 6, "炽焰源石虫": 23}, - "result": "left", - } + initial_battle_setup = {"left": {"炮击组长": 3, "“庞贝”": 2}, "right": {"沸血骑士团精锐": 6, "炽焰源石虫": 23}, + "result": "left"} - sys.stdin.reconfigure(encoding="utf-8") + sys.stdin.reconfigure(encoding='utf-8') try: if not sys.stdin.isatty(): json_data = sys.stdin.read() @@ -683,9 +576,7 @@ def main(): else: print("stdin 是交互式终端,使用默认配置。") except json.JSONDecodeError: - print( - "错误: 无法解析 stdin 中的 JSON 数据,请检查格式,将使用默认配置。" - ) + print("错误: 无法解析 stdin 中的 JSON 数据,请检查格式,将使用默认配置。") except Exception as e: print(f"从 stdin 读取或解析时发生未知错误: {e},将使用默认配置。") @@ -696,37 +587,24 @@ def main(): if not app.monster_data: # 检查 monsters.json 是否加载成功 error_messages.append("错误: monsters.json 未找到或为空,请检查文件!") - if ( - len(initial_battle_setup.get("left", {})) == 0 - or len(initial_battle_setup.get("right", {})) == 0 - ): + if len(initial_battle_setup.get("left", {})) == 0 or len(initial_battle_setup.get("right", {})) == 0: error_messages.append("错误:至少一方的怪物列表为空!") problematic_monsters_found = [] - problematic_monster_names = [ - "矿脉守卫", - "提亚卡乌好战者", - "凋零萨卡兹", - "狂暴宿主组长", - "高能源石虫", - ] + problematic_monster_names = ["矿脉守卫", "提亚卡乌好战者", "凋零萨卡兹", "狂暴宿主组长", "高能源石虫"] for team_key in ["left", "right"]: for monster_name in initial_battle_setup.get(team_key, {}): if monster_name in problematic_monster_names: problematic_monsters_found.append( - f"{('左方' if team_key == 'left' else '右方')}存在问题怪物: {monster_name}" - ) + f"{('左方' if team_key == 'left' else '右方')}存在问题怪物: {monster_name}") if problematic_monsters_found: - error_messages.append( - "警告: " + "; ".join(problematic_monsters_found) - ) + error_messages.append("警告: " + "; ".join(problematic_monsters_found)) if error_messages: - app.show_message_below_button( - " | ".join(error_messages), is_error=True, duration=10000 - ) + app.show_message_below_button(" | ".join( + error_messages), is_error=True, duration=10000) if "monsters.json 未找到或为空" in " | ".join(error_messages): print("由于 monsters.json 缺失或错误,模拟器可能无法正常工作。") diff --git a/src/analysis/similar_history_match.py b/similar_history_match.py similarity index 74% rename from src/analysis/similar_history_match.py rename to similar_history_match.py index 286194f..ea9f1ff 100644 --- a/src/analysis/similar_history_match.py +++ b/similar_history_match.py @@ -1,9 +1,7 @@ import numpy as np import pandas as pd -from src.core.config import MONSTER_COUNT -from src.core.config import FIELD_FEATURE_COUNT -from src.core.paths import PROJECT_ROOT - +from config import MONSTER_COUNT +from config import FIELD_FEATURE_COUNT def cosine_similarity_manual(a, b): """手动实现余弦相似度,替代 sklearn 以减小打包体积""" @@ -15,11 +13,10 @@ def cosine_similarity_manual(a, b): dot = np.dot(a, b.T) return dot / (norm_a * norm_b.T) - class HistoryMatch: """错题本数据集的读取和处理类""" - def __init__(self, csv_path=PROJECT_ROOT / "data" / "arknights.csv"): + def __init__(self, csv_path="arknights.csv"): # 初始化时加载历史对局数据 self.csv_path = csv_path self.load_history_data() @@ -32,66 +29,43 @@ def load_history_data(self): """读取 CSV 文件,加载历史对局的左右阵容、地形及胜负标签""" try: df = pd.read_csv(self.csv_path, header=None, skiprows=1) - + # 新数据格式: [怪物L(77), 场地L(6), 怪物R(77), 场地R(6), Result, ImgPath] total_features = (MONSTER_COUNT + FIELD_FEATURE_COUNT) * 2 - + if df.shape[1] >= total_features + 1: # 至少包含特征和结果列 # 提取各部分特征 left_monster_end = MONSTER_COUNT left_field_end = MONSTER_COUNT + FIELD_FEATURE_COUNT - right_monster_end = ( - MONSTER_COUNT + FIELD_FEATURE_COUNT + MONSTER_COUNT - ) - right_field_end = ( - MONSTER_COUNT - + FIELD_FEATURE_COUNT - + MONSTER_COUNT - + FIELD_FEATURE_COUNT - ) - + right_monster_end = MONSTER_COUNT + FIELD_FEATURE_COUNT + MONSTER_COUNT + right_field_end = MONSTER_COUNT + FIELD_FEATURE_COUNT + MONSTER_COUNT + FIELD_FEATURE_COUNT + # 分别提取怪物和地形特征 - left_monsters = df.iloc[:, 0:left_monster_end].values.astype( - float - ) - left_terrain = df.iloc[ - :, left_monster_end:left_field_end - ].values.astype(float) - right_monsters = df.iloc[ - :, left_field_end:right_monster_end - ].values.astype(float) - right_terrain = df.iloc[ - :, right_monster_end:right_field_end - ].values.astype(float) - + left_monsters = df.iloc[:, 0:left_monster_end].values.astype(float) + left_terrain = df.iloc[:, left_monster_end:left_field_end].values.astype(float) + right_monsters = df.iloc[:, left_field_end:right_monster_end].values.astype(float) + right_terrain = df.iloc[:, right_monster_end:right_field_end].values.astype(float) + # 合并怪物特征(只使用怪物部分进行相似度计算) self.past_left = left_monsters self.past_right = right_monsters - + # 保存地形特征用于显示 self.past_left_terrain = left_terrain self.past_right_terrain = right_terrain - + # 胜负标签 self.labels = df.iloc[:, total_features].values else: # 兼容旧格式:只有怪物特征 - self.past_left = df.iloc[:, 0:MONSTER_COUNT].values.astype( - float - ) - self.past_right = df.iloc[ - :, MONSTER_COUNT : MONSTER_COUNT * 2 - ].values.astype(float) - self.labels = df.iloc[:, MONSTER_COUNT * 2].values - + self.past_left = df.iloc[:, 0:MONSTER_COUNT].values.astype(float) + self.past_right = df.iloc[:, MONSTER_COUNT:MONSTER_COUNT*2].values.astype(float) + self.labels = df.iloc[:, MONSTER_COUNT*2].values + # 地形特征为空 - self.past_left_terrain = np.zeros( - (len(self.past_left), FIELD_FEATURE_COUNT) - ) - self.past_right_terrain = np.zeros( - (len(self.past_right), FIELD_FEATURE_COUNT) - ) - + self.past_left_terrain = np.zeros((len(self.past_left), FIELD_FEATURE_COUNT)) + self.past_right_terrain = np.zeros((len(self.past_right), FIELD_FEATURE_COUNT)) + except Exception as e: print(f"加载历史数据失败: {e}") # 加载失败时,初始化为空数组 @@ -102,18 +76,14 @@ def load_history_data(self): self.labels = np.array([], dtype=str) # 构造历史对局特征: 左右数量之和与差的绝对值拼接(只使用怪物特征) - self.feat_past = np.hstack( - [ - self.past_left + self.past_right, - np.abs(self.past_left - self.past_right), - ] - ) + self.feat_past = np.hstack([ + self.past_left + self.past_right, + np.abs(self.past_left - self.past_right) + ]) # 历史对局总数 self.N_history = self.past_left.shape[0] - def render_similar_matches( - self, left_counts: np.ndarray, right_counts: np.ndarray - ): + def render_similar_matches(self, left_counts: np.ndarray, right_counts: np.ndarray): """返回与当前对局最相似的历史对局索引及胜率统计""" # 将输入转为浮点型数组 cur_left = left_counts.astype(float) @@ -126,9 +96,7 @@ def render_similar_matches( need_R_idx = np.nonzero(pres_R)[0] # 当前右侧有兵的索引 # 构造当前对局特征并计算与所有历史的余弦相似度 - feat_cur = np.hstack( - [cur_left + cur_right, np.abs(cur_left - cur_right)] - ).reshape(1, -1) + feat_cur = np.hstack([cur_left + cur_right, np.abs(cur_left - cur_right)]).reshape(1, -1) sims = cosine_similarity_manual(feat_cur, self.feat_past)[0] # 历史对局的存在布尔矩阵 @@ -137,18 +105,14 @@ def render_similar_matches( # 计算未镜像(missA, cntA)和镜像后(missB, cntB)的缺兵及数量差距 missA = np.sum(np.logical_xor(pres_L, hist_pres_L), axis=1) + np.sum( - np.logical_xor(pres_R, hist_pres_R), axis=1 - ) + np.logical_xor(pres_R, hist_pres_R), axis=1) cntA = np.sum(np.abs(self.past_left - cur_left), axis=1) + np.sum( - np.abs(self.past_right - cur_right), axis=1 - ) + np.abs(self.past_right - cur_right), axis=1) missB = np.sum(np.logical_xor(pres_L, hist_pres_R), axis=1) + np.sum( - np.logical_xor(pres_R, hist_pres_L), axis=1 - ) + np.logical_xor(pres_R, hist_pres_L), axis=1) cntB = np.sum(np.abs(self.past_right - cur_left), axis=1) + np.sum( - np.abs(self.past_left - cur_right), axis=1 - ) + np.abs(self.past_left - cur_right), axis=1) # 根据(miss, cnt)比较,决定是否对历史数据做镜像处理 swap = (missB < missA) | ((missB == missA) & (cntB < cntA)) @@ -167,27 +131,18 @@ def render_similar_matches( full_R = np.all(Rh[:, need_R_idx] == cur_right[need_R_idx], axis=1) # 计算需求索引处的数量差和 - diff_L = np.sum( - np.abs(Lh[:, need_L_idx] - cur_left[need_L_idx]), axis=1 - ) - diff_R = np.sum( - np.abs(Rh[:, need_R_idx] - cur_right[need_R_idx]), axis=1 - ) + diff_L = np.sum(np.abs(Lh[:, need_L_idx] - cur_left[need_L_idx]), axis=1) + diff_R = np.sum(np.abs(Rh[:, need_R_idx] - cur_right[need_R_idx]), axis=1) # 计算对手兵种在本方需求中的命中数,取最小值作为 match_other - hit_L = np.sum( - hist_pres_Rh[:, need_L_idx] & pres_L[need_L_idx], axis=1 - ) - hit_R = np.sum( - hist_pres_Lh[:, need_R_idx] & pres_R[need_R_idx], axis=1 - ) + hit_L = np.sum(hist_pres_Rh[:, need_L_idx] & pres_L[need_L_idx], axis=1) + hit_R = np.sum(hist_pres_Lh[:, need_R_idx] & pres_R[need_R_idx], axis=1) match_other = np.minimum(hit_L, hit_R) # 根据命中侧及是否完全匹配,选择对应的 qdiff_other qdiff_other = np.where( - (hit_R > 0) & (~full_R), - diff_R, - np.where((hit_L > 0) & (~full_L), diff_L, 0), + (hit_R > 0) & (~full_R), diff_R, + np.where((hit_L > 0) & (~full_L), diff_L, 0) ) # 批量计算分类所需的布尔向量 @@ -216,9 +171,7 @@ def render_similar_matches( # 且左右两侧比例相同,且比例不为 1(确保“数量均不同”) # 注意:当某侧不存在任意单位时,认为该侧比例为 1 且恒定 if need_L_idx.size > 0: - ratios_L = Lh[:, need_L_idx] / np.maximum( - cur_left[need_L_idx], 1e-12 - ) + ratios_L = Lh[:, need_L_idx] / np.maximum(cur_left[need_L_idx], 1e-12) rL_min = ratios_L.min(axis=1) rL_max = ratios_L.max(axis=1) uniform_L = np.isclose(rL_min, rL_max, rtol=1e-3, atol=1e-6) @@ -228,9 +181,7 @@ def render_similar_matches( rL = np.ones(self.N_history, dtype=float) if need_R_idx.size > 0: - ratios_R = Rh[:, need_R_idx] / np.maximum( - cur_right[need_R_idx], 1e-12 - ) + ratios_R = Rh[:, need_R_idx] / np.maximum(cur_right[need_R_idx], 1e-12) rR_min = ratios_R.min(axis=1) rR_max = ratios_R.max(axis=1) uniform_R = np.isclose(rR_min, rR_max, rtol=1e-3, atol=1e-6) @@ -240,9 +191,7 @@ def render_similar_matches( rR = np.ones(self.N_history, dtype=float) same_ratio = np.isclose(rL, rR, rtol=1e-3, atol=1e-6) - ratio_not_one = ~np.isclose( - rL, 1.0, rtol=1e-3, atol=1e-6 - ) # rL==rR 时即可代表两侧都不为1 + ratio_not_one = ~np.isclose(rL, 1.0, rtol=1e-3, atol=1e-6) # rL==rR 时即可代表两侧都不为1 proportional = uniform_L & uniform_R & same_ratio & ratio_not_one # 2类:同种类,数量均不同且成比例 @@ -266,44 +215,14 @@ def render_similar_matches( # 从前5条中计算左右胜率 top5 = top20[:5] - labs = np.where( - swap[top5], - np.where(self.labels[top5] == "L", "R", "L"), - self.labels[top5], - ) - tgtL = ( - need_L_idx[np.argmax(cur_left[need_L_idx])] - if need_L_idx.size - else None - ) - tgtR = ( - need_R_idx[np.argmax(cur_right[need_R_idx])] - if need_R_idx.size - else None - ) - - lw = np.sum( - [ - lab - == ( - "L" - if (Lh[i, tgtL] if tgtL is not None else 0) > 0 - else "R" - ) - for i, lab in zip(top5, labs) - ] - ) - rw = np.sum( - [ - lab - == ( - "L" - if (Lh[i, tgtR] if tgtR is not None else 0) > 0 - else "R" - ) - for i, lab in zip(top5, labs) - ] - ) + labs = np.where(swap[top5], np.where(self.labels[top5]=="L", "R", "L"), self.labels[top5]) + tgtL = need_L_idx[np.argmax(cur_left[need_L_idx])] if need_L_idx.size else None + tgtR = need_R_idx[np.argmax(cur_right[need_R_idx])] if need_R_idx.size else None + + lw = np.sum([lab == ("L" if (Lh[i, tgtL] if tgtL is not None else 0) > 0 else "R") + for i, lab in zip(top5, labs)]) + rw = np.sum([lab == ("L" if (Lh[i, tgtR] if tgtR is not None else 0) > 0 else "R") + for i, lab in zip(top5, labs)]) self.left_rate = lw / len(top5) if top5.size else 0 self.right_rate = rw / len(top5) if top5.size else 0 self.sims = sims @@ -316,24 +235,19 @@ def get_terrain_names(self, idx, is_swapped=False): """获取指定历史对局的地形名称""" if idx >= len(self.past_left_terrain): return "无地形" - + # 根据是否镜像选择地形特征 - terrain_features = ( - self.past_right_terrain[idx] - if is_swapped - else self.past_left_terrain[idx] - ) - + terrain_features = self.past_right_terrain[idx] if is_swapped else self.past_left_terrain[idx] + # 获取激活的地形特征索引 active_indices = np.where(terrain_features > 0)[0] - + if len(active_indices) == 0: return "无地形" - + # 尝试从FieldRecognizer获取实际的特征列名称 try: - from src.recognition.field_recognition import FieldRecognizer - + from field_recognition import FieldRecognizer field_recognizer = FieldRecognizer() if field_recognizer.is_ready(): feature_columns = field_recognizer.get_feature_columns() @@ -384,13 +298,11 @@ def get_terrain_names(self, idx, is_swapped=False): # 如果无法识别,使用原名称的简化版本 simple_name = full_name.replace("_", "") active_terrains.append(simple_name) - - return ( - "+".join(active_terrains) if active_terrains else "无地形" - ) + + return "+".join(active_terrains) if active_terrains else "无地形" except Exception: pass - + # 备用硬编码映射(如果无法获取FieldRecognizer) # 与main.py的terrain_display_mapping保持一致 terrain_names = { @@ -405,14 +317,14 @@ def get_terrain_names(self, idx, is_swapped=False): 8: "顶部弩炮", 9: "侧边弩炮", 10: "侧边火炮", - 11: "顶部火炮", + 11: "顶部火炮" } - + # 获取所有激活地形的名称 active_terrains = [] for i in active_indices: if i < len(terrain_names): active_terrains.append(terrain_names[i]) - + # 如果有多个地形,用"+"连接 return "+".join(active_terrains) if active_terrains else "无地形" diff --git a/src/ui/similar_history_match_ui.py b/simular_history_match_ui.py similarity index 85% rename from src/ui/similar_history_match_ui.py rename to simular_history_match_ui.py index dd8753b..6ddf18c 100644 --- a/src/ui/similar_history_match_ui.py +++ b/simular_history_match_ui.py @@ -1,20 +1,11 @@ from PyQt6.QtCore import Qt -from PyQt6.QtWidgets import ( - QWidget, - QVBoxLayout, - QHBoxLayout, - QLabel, - QScrollArea, - QGraphicsDropShadowEffect, - QFrame, -) -from PyQt6.QtGui import QPixmap, QColor +from PyQt6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QScrollArea, QGraphicsDropShadowEffect, QFrame +from PyQt6.QtGui import QPixmap, QImage, QFont, QIcon, QPainter, QColor import numpy as np import logging -from src.analysis.similar_history_match import HistoryMatch -from src.core.config import MONSTER_COUNT, MONSTER_DATA -from src.core.paths import image_path +from similar_history_match import HistoryMatch +from config import MONSTER_COUNT, MONSTER_DATA logger = logging.getLogger(__name__) @@ -33,7 +24,8 @@ def init_ui(self): self.history_scroll_area = QScrollArea() self.history_scroll_area.setFixedWidth(540) self.history_scroll_area.setWidgetResizable(True) - self.history_scroll_area.setStyleSheet(""" + self.history_scroll_area.setStyleSheet( + """ QScrollBar:horizontal { background: rgba(0, 0, 0, 0); width: 12px; /* 宽度 */ @@ -78,7 +70,8 @@ def init_ui(self): min-height: 20px; border-radius: 6px; } - """) + """ + ) # 创建内容部件 self.history_widget = QWidget() @@ -121,28 +114,26 @@ def render_similar_matches(self, left_monsters, right_monsters): shadow.setOffset(2) # 偏移量(0表示均匀四周发光) title_label.setGraphicsEffect(shadow) - title_label.setStyleSheet(""" + title_label.setStyleSheet( + """ QWidget { border-radius: 0px; font-size: 24px; font-weight: bold; color: white; } - """) + """ + ) self.history_layout.addWidget(title_label) # 渲染每个历史对局 for idx in top_indices: - self.add_history_match( - idx, sims[idx], left_monsters, right_monsters - ) + self.add_history_match(idx, sims[idx], left_monsters, right_monsters) except Exception as e: logger.error(f"渲染历史对局失败: {str(e)}") - def add_history_match( - self, idx, similarity, left_monsters, right_monsters - ): + def add_history_match(self, idx, similarity, left_monsters, right_monsters): """添加单个历史对局到面板""" # 获取历史数据 left = self.history_match.past_left[idx] @@ -168,23 +159,25 @@ def add_history_match( setR_past = set(np.where(right > 0)[0]) # 判断是否需要镜像历史对局 - should_swap = ( - len(setL_cur ^ setR_past) + len(setR_cur ^ setL_past) - ) < (len(setL_cur ^ setL_past) + len(setR_cur ^ setR_past)) + should_swap = (len(setL_cur ^ setR_past) + len(setR_cur ^ setL_past)) < ( + len(setL_cur ^ setL_past) + len(setR_cur ^ setR_past) + ) # 获取地形名称 terrain_name = self.history_match.get_terrain_names(idx, should_swap) # 创建对局容器 match_widget = QWidget() - match_widget.setStyleSheet(""" + match_widget.setStyleSheet( + """ QWidget { background-color: rgba(50, 50, 50, 150); border-radius: 10px; padding: 0px; margin: 5px; } - """) + """ + ) match_widget.setFixedSize(500, 170) # 增加高度以容纳地形信息 match_layout = QVBoxLayout(match_widget) @@ -206,7 +199,8 @@ def add_history_match( # 添加地形信息显示 terrain_label = QLabel(f"地形: {terrain_name}") - terrain_label.setStyleSheet(""" + terrain_label.setStyleSheet( + """ QLabel { color: #CCCCCC; font: 10px Microsoft YaHei; @@ -215,7 +209,8 @@ def add_history_match( border-radius: 3px; margin: 2px; } - """) + """ + ) terrain_label.setAlignment(Qt.AlignmentFlag.AlignCenter) match_layout.addWidget(terrain_label) @@ -224,14 +219,16 @@ def add_history_match( def create_team_widget(self, side, counts, is_winner): """创建单个队伍显示部件""" team_widget = QWidget() - team_widget.setStyleSheet(f""" + team_widget.setStyleSheet( + f""" QWidget {{ background-color: {'rgba(250, 250, 50, 150)' if is_winner else 'rgba(50, 50, 50, 100)'}; border-radius: 8px; padding: 0px; margin: 0px; }} - """) + """ + ) layout = QVBoxLayout(team_widget) @@ -243,14 +240,16 @@ def create_team_widget(self, side, counts, is_winner): shadow01.setOffset(3) # 偏移量(0表示均匀四周发光) ops_widget.setGraphicsEffect(shadow01) - ops_widget.setStyleSheet(""" + ops_widget.setStyleSheet( + """ QWidget { background-color: rgba(0, 0, 0, 0); border-radius: 0px; padding: 0px; margin: 0px; } - """) + """ + ) ops_layout = QHBoxLayout(ops_widget) ops_layout.setSpacing(5) ops_layout.setContentsMargins(0, 0, 0, 0) @@ -259,9 +258,7 @@ def create_team_widget(self, side, counts, is_winner): if count > 0: # 创建干员显示 op_widget = QWidget() - op_widget.setStyleSheet( - "background-color: rgba(0, 0, 0, 0); padding: 0px 0;margin: 0px;" - ) + op_widget.setStyleSheet("background-color: rgba(0, 0, 0, 0); padding: 0px 0;margin: 0px;") op_layout = QVBoxLayout(op_widget) op_layout.setContentsMargins(0, 0, 0, 0) op_layout.setAlignment(Qt.AlignmentFlag.AlignCenter) @@ -271,15 +268,10 @@ def create_team_widget(self, side, counts, is_winner): img_label.setFixedSize(60, 60) img_label.setAlignment(Qt.AlignmentFlag.AlignCenter) try: - pixmap = QPixmap( - str(image_path(MONSTER_DATA['原始名称'][i + 1])) - ) + pixmap = QPixmap(f"images/{MONSTER_DATA['原始名称'][i+1]}.png") if not pixmap.isNull(): pixmap = pixmap.scaled( - 60, - 60, - Qt.AspectRatioMode.KeepAspectRatio, - Qt.TransformationMode.SmoothTransformation, + 60, 60, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation ) img_label.setPixmap(pixmap) except: @@ -288,11 +280,13 @@ def create_team_widget(self, side, counts, is_winner): # 数量标签 count_label = QLabel(str(int(count))) count_label.setAlignment(Qt.AlignmentFlag.AlignCenter) - count_label.setStyleSheet(""" + count_label.setStyleSheet( + """ color: #EDEDED; font: bold 20px SimHei; min-width: 20px; - """) + """ + ) op_layout.addWidget(img_label, stretch=3) op_layout.addWidget(count_label, stretch=1) diff --git a/simulator/.gitignore b/simulator/.gitignore new file mode 100644 index 0000000..763624e --- /dev/null +++ b/simulator/.gitignore @@ -0,0 +1 @@ +__pycache__/* \ No newline at end of file diff --git a/src/simulation/README.md b/simulator/README.md similarity index 100% rename from src/simulation/README.md rename to simulator/README.md diff --git a/src/core/__init__.py b/simulator/__init__.py similarity index 100% rename from src/core/__init__.py rename to simulator/__init__.py diff --git a/src/simulation/arknights.csv b/simulator/arknights.csv similarity index 100% rename from src/simulation/arknights.csv rename to simulator/arknights.csv diff --git a/src/simulation/battle_field.py b/simulator/battle_field.py similarity index 60% rename from src/simulation/battle_field.py rename to simulator/battle_field.py index 81b7828..7ba4abd 100644 --- a/src/simulation/battle_field.py +++ b/simulator/battle_field.py @@ -1,6 +1,9 @@ +import json +import math import random import time import numpy as np +from enum import Enum import logging from typing import TYPE_CHECKING @@ -11,13 +14,9 @@ if TYPE_CHECKING: from .monsters import Monster - + from .monsters import MonsterFactory -from .utils import ( - VIRTUAL_TIME_DELTA, - Faction, - SpatialHash, -) +from .utils import VIRTUAL_TIME_DELTA, BuffEffect, BuffType, Faction, SpatialHash from .zone import PoisonZone # 场景参数 @@ -25,14 +24,14 @@ SPAWN_AREA = 2 # 阵营出生区域宽度 +from collections import defaultdict from .projectiles import ProjectileManager - class Battlefield: def __init__(self, monster_data): - self.monsters: list[Monster] = [] - self.alive_monsters: list[Monster] = [] - self.hash_grid: SpatialHash = SpatialHash(self, cell_size=0.5) + self.monsters : list[Monster] = [] + self.alive_monsters : list[Monster] = [] + self.hash_grid : SpatialHash = SpatialHash(self, cell_size=0.5) self.HIT_BOX_RADIUS = 0.2 self.round = 0 @@ -52,105 +51,89 @@ def __init__(self, monster_data): self.current_spawn_left = 0 self.current_spawn_right = 0 - def query_monster(self, target_position, radius) -> list["Monster"]: + def query_monster(self, target_position, radius) -> list['Monster']: results = [] if len(self.alive_monsters) < (radius / self.hash_grid.cell_size) ** 2: for m in self.alive_monsters: - if ( - m.is_alive - and (m.position - target_position).magnitude <= radius - ): + if m.is_alive and (m.position - target_position).magnitude <= radius: results.append(m) else: - for monster_id in self.hash_grid.query_neighbors( - target_position, radius - ): - m = self.get_monster_with_id(monster_id) - if ( - m.is_alive - and (m.position - target_position).magnitude <= radius - ): + for id in self.hash_grid.query_neighbors(target_position, radius): + m = self.get_monster_with_id(id) + if m.is_alive and (m.position - target_position).magnitude <= radius: results.append(m) return results - def append_monster(self, monster: "Monster"): + def append_monster(self, monster : 'Monster'): """添加一个怪物到战场""" - monster_id = self.globalId - monster.id = monster_id + id = self.globalId + monster.id = id self.globalId += 1 self.monsters.append(monster) self.hash_grid.insert(monster.position, monster.id) - - def append_monster_name(self, name, faction, pos) -> "Monster": + + def append_monster_name(self, name, faction, pos) -> 'Monster': """添加一个怪物到战场,只需要名字""" data = next((m for m in self.monster_data if m["名字"] == name), None) - monster_id = self.globalId + id = self.globalId monster = MonsterFactory.create_monster(data, faction, pos, self) - monster.id = monster_id + monster.id = id self.globalId += 1 self.monsters.append(monster) self.hash_grid.insert(monster.position, monster.id) return monster - def get_monster_with_id(self, monster_id) -> "Monster": - return self.monsters[monster_id] - + def get_monster_with_id(self, id) -> 'Monster': + return self.monsters[id] + def setup_battle(self, left_army, right_army, monster_data): """二维战场初始化""" # 左阵营生成在左上区域 - for name, count in left_army.items(): + for (name, count) in left_army.items(): data = next((m for m in monster_data if m["名字"] == name), None) if data is None: raise ValueError(f"左侧怪物 {name} 在 monster_data 中未找到!") for _ in range(count): pos = FastVector( - random.uniform(0, 0.5), random.uniform(0, MAP_SIZE[1]) - ) - self.monster_temporal_area_left.append( - MonsterFactory.create_monster( - data, Faction.LEFT, pos, self - ) + random.uniform(0, 0.5), + random.uniform(0, MAP_SIZE[1]) ) + self.monster_temporal_area_left.append( MonsterFactory.create_monster(data, Faction.LEFT, pos, self)) # 右阵营生成在右下区域 - for name, count in right_army.items(): + for (name, count) in right_army.items(): data = next((m for m in monster_data if m["名字"] == name), None) if data is None: raise ValueError(f"右侧怪物 {name} 在 monster_data 中未找到!") for _ in range(count): pos = FastVector( - random.uniform(MAP_SIZE[0] - 0.5, MAP_SIZE[0]), - random.uniform(0, MAP_SIZE[1]), - ) - self.monster_temporal_area_right.append( - MonsterFactory.create_monster( - data, Faction.RIGHT, pos, self - ) + random.uniform(MAP_SIZE[0]-0.5, MAP_SIZE[0]), + random.uniform(0, MAP_SIZE[1]) ) + self.monster_temporal_area_right.append(MonsterFactory.create_monster(data, Faction.RIGHT, pos, self)) self.alive_monsters = self.monsters self.gameTime = 0 + self.current_spawn = 0 random.shuffle(self.monster_temporal_area_left) random.shuffle(self.monster_temporal_area_right) return True def check_victory(self): """检查胜利条件""" - if self.current_spawn_left < len( - self.monster_temporal_area_left - ) or self.current_spawn_right < len(self.monster_temporal_area_right): + if self.current_spawn_left < len(self.monster_temporal_area_left) or self.current_spawn_right < len(self.monster_temporal_area_right): return None alive_factions = set() for m in self.alive_monsters: if m.is_alive: alive_factions.add(m.faction) - + if len(alive_factions) == 1: return list(alive_factions)[0] elif len(alive_factions) == 0: return Faction.LEFT return None - + def check_zone(self): new_zone = [] # 检查场地效果 @@ -168,23 +151,12 @@ def run_one_frame(self): self.round += 1 if self.round < 40 or self.round > 90: - if ( - self.current_spawn_left < len(self.monster_temporal_area_left) - and self.round % 2 == 0 - ): - self.append_monster( - self.monster_temporal_area_left[self.current_spawn_left] - ) + if self.current_spawn_left < len(self.monster_temporal_area_left) and self.round % 2 == 0: + self.append_monster(self.monster_temporal_area_left[self.current_spawn_left]) self.current_spawn_left += 1 - if ( - self.current_spawn_right - < len(self.monster_temporal_area_right) - and self.round % 2 == 0 - ): - self.append_monster( - self.monster_temporal_area_right[self.current_spawn_right] - ) + if self.current_spawn_right < len(self.monster_temporal_area_right) and self.round % 2 == 0: + self.append_monster(self.monster_temporal_area_right[self.current_spawn_right]) self.current_spawn_right += 1 self.check_zone() @@ -200,27 +172,21 @@ def run_one_frame(self): self.alive_monsters = [m for m in self.monsters if m.is_alive] winner = self.check_victory() if winner: - logger.info("\nVictory for %s!", winner.name) - left = len( - [ - m - for m in self.alive_monsters - if m.is_alive and m.faction == Faction.LEFT - ] - ) - logger.info("左边存活%s / 右边存活%s", left, len(self.alive_monsters) - left) + logger.info(f"\nVictory for {winner.name}!") + left = len([m for m in self.alive_monsters if m.is_alive and m.faction == Faction.LEFT]) + logger.info(f"左边存活{left} / 右边存活{len(self.alive_monsters) - left}") return winner - + self.gameTime += VIRTUAL_TIME_DELTA return None - + def run_battle(self, visualize=False): """运行战斗直到决出胜负""" while True: if visualize and self.round % 30 == 0: self.print_battlefield() time.sleep(1) - + result = self.run_one_frame() if result != None: return result @@ -229,33 +195,29 @@ def danger_zone_size(self): if self.gameTime < 40: return 0 return int((self.gameTime - 40) / 20) + 1 - + def add_new_zone(self, zone): self.effect_zones.append(zone) def print_battlefield(self): """二维战场可视化""" - grid = np.full((MAP_SIZE[1] * 2, MAP_SIZE[0] * 2), ".", dtype="U2") - + grid = np.full((MAP_SIZE[1] * 2, MAP_SIZE[0] * 2), '.', dtype='U2') + for m in self.alive_monsters: if m.is_alive: - x = np.minimum( - np.maximum(0, int(m.position.x * 2)), MAP_SIZE[0] * 2 - 1 - ) - y = np.minimum( - np.maximum(0, int(m.position.y * 2)), MAP_SIZE[1] * 2 - 1 - ) - symbol = "L" if m.faction == Faction.LEFT else "R" - if grid[y, x] != "." and symbol != grid[y, x]: - symbol = "X" + x = np.minimum(np.maximum(0, int(m.position.x * 2)), MAP_SIZE[0]*2-1) + y = np.minimum(np.maximum(0, int(m.position.y * 2)), MAP_SIZE[1]*2-1) + symbol = 'L' if m.faction == Faction.LEFT else 'R' + if grid[y, x] != '.' and symbol != grid[y, x]: + symbol = 'X' if m.char_icon != "": symbol = m.char_icon grid[y, x] = symbol - - logger.info("\nRound %s", self.round) + + logger.info(f"\nRound {self.round}") for row in grid: - logger.info(" ".join(row)) + logger.info(' '.join(row)) def get_grid(self, target): x, y = int(target.position.x), int(target.position.y) - return x, y + return x, y \ No newline at end of file diff --git a/src/simulation/elemental.py b/simulator/elemental.py similarity index 82% rename from src/simulation/elemental.py rename to simulator/elemental.py index 2e00207..41a0c90 100644 --- a/src/simulation/elemental.py +++ b/simulator/elemental.py @@ -1,9 +1,13 @@ +from enum import Enum +import math + +import numpy as np + from .utils import DamageType, ElementType, debug_print, lerp class ElementAccumulator: """多元素损伤容器""" - def __init__(self, owner): self.accumulators = {et: 0.0 for et in ElementType} self.active_burst = None @@ -14,17 +18,15 @@ def accumulate(self, element: ElementType, value: float): """累积元素损伤""" if self.active_burst: return # 爆条期间暂停累积 - + self.accumulators[element] += value limits = 2000 if self.owner.boss else 1000 if self.accumulators[element] >= limits: self.accumulators[element] = 0 self.active_burst = ElementBurst(element, self.owner) - class ElementBurst: """爆条效果控制器""" - def __init__(self, trigger_element: ElementType, owner): self.owner = owner self.start_time = owner.battlefield.gameTime @@ -49,20 +51,14 @@ def _init_effect_params(self): elif self.trigger_element == ElementType.FIRE: self.duration = 10 dmg = 7000 - debug_print( - f"{self.owner.name}{self.owner.id} 灼燃发期间受到{dmg}点伤害" - ) + debug_print(f"{self.owner.name}{self.owner.id} 灼燃发期间受到{dmg}点伤害") self.owner.take_damage(dmg, DamageType.TRUE) self.owner.magic_resist -= 20 @property def progress(self): """效果进度百分比""" - return min( - 1.0, - (self.owner.battlefield.gameTime - self.start_time) - / self.duration, - ) + return min(1.0, (self.owner.battlefield.gameTime - self.start_time) / self.duration) def on_clear(self): if self.trigger_element == ElementType.FIRE: @@ -73,27 +69,19 @@ def update_effect(self, deltaTime): if self.trigger_element == ElementType.NECRO_RIGHT: # 虚弱效果衰减 self.owner.attack_multiplier = lerp(0.5, 1, self.progress) - + # 持续伤害应用 self.dot_timer += deltaTime - self.owner.take_damage( - self.dot_damage * deltaTime, DamageType.TRUE - ) + self.owner.take_damage(self.dot_damage * deltaTime, DamageType.TRUE) if self.dot_timer >= 1.0: - debug_print( - f"{self.owner.name}{self.owner.id} 凋亡损伤爆发期间受到{self.dot_damage}伤害" - ) + debug_print(f"{self.owner.name}{self.owner.id} 凋亡损伤爆发期间受到{self.dot_damage}伤害") self.dot_timer = 0 elif self.trigger_element == ElementType.NECRO_LEFT: # 持续伤害应用 self.dot_timer += deltaTime - self.owner.take_damage( - self.dot_damage * deltaTime, DamageType.TRUE - ) + self.owner.take_damage(self.dot_damage * deltaTime, DamageType.TRUE) if self.dot_timer >= 1.0: - debug_print( - f"{self.owner.name}{self.owner.id} 凋亡损伤爆发期间受到{self.dot_damage}伤害" - ) + debug_print(f"{self.owner.name}{self.owner.id} 凋亡损伤爆发期间受到{self.dot_damage}伤害") self.dot_timer = 0 @@ -103,25 +91,25 @@ def update_effect(self, deltaTime): # self.resistances = {et: 0.0 for et in ElementType} # self.active_effects = [] # self.hp = max_hp - + # def take_element_damage(self, element: ElementType, base_damage: float): # # 计算实际伤害(考虑抗性) # resistance = self.resistances.get(element, 0) # actual_damage = base_damage * (1 - resistance) # self.hp -= actual_damage - + # # 累积30%基础伤害作为元素损伤 # self.element_system.accumulate(element, base_damage * 0.3) - + # def set_element_resistance(self, element: ElementType, value: float): # """设置元素抗性(0.0-1.0)""" # self.resistances[element] = max(0, min(1.0, value)) - + # def update(self): # """每帧更新状态""" # # 处理爆条队列 # self.element_system.process_burst() - + # # 更新激活中的爆条效果 # if burst := self.element_system.active_burst: # if time.time() - burst.start_time > burst.duration: @@ -133,4 +121,4 @@ def update_effect(self, deltaTime): # """清除爆条残留效果""" # self.element_system.active_burst = None # for elem in ElementType: -# self.resistances[elem] = 0.0 +# self.resistances[elem] = 0.0 \ No newline at end of file diff --git a/src/simulation/monsters.json b/simulator/monsters.json similarity index 100% rename from src/simulation/monsters.json rename to simulator/monsters.json diff --git a/src/simulation/monsters.py b/simulator/monsters.py similarity index 80% rename from src/simulation/monsters.py rename to simulator/monsters.py index 7878046..d9a1122 100644 --- a/src/simulation/monsters.py +++ b/simulator/monsters.py @@ -1,5 +1,8 @@ +from dataclasses import dataclass, field +import json import math import random +import time from enum import Enum from typing import List import numpy as np @@ -11,16 +14,10 @@ from .projectiles import AOEType, AOE炸弹, AOE炸弹锁定 if TYPE_CHECKING: - from .battle_field import Battlefield + from battle_field import Battlefield from .elemental import ElementAccumulator, ElementType -from .utils import ( - BuffEffect, - BuffType, - DamageType, - calculate_normal_dmg, - debug_print, -) +from .utils import BuffEffect, BuffType, DamageType, calculate_normal_dmg, debug_print, Faction from .zone import WineZone @@ -31,7 +28,7 @@ class AttackState(Enum): class AttackAnimation: - def __init__(self, 前摇时间, 后摇时间, 等待时间, monster: "Monster"): + def __init__(self, 前摇时间, 后摇时间, 等待时间, monster: 'Monster'): self.前摇时间 = 前摇时间 self.后摇时间 = 后摇时间 self.等待时间 = 等待时间 @@ -52,13 +49,7 @@ def idle_time(self): class TargetSelector: @staticmethod - def select_targets( - attacker, - battlefield, - need_in_range=False, - max_targets=2, - reverse=False, - ): + def select_targets(attacker, battlefield, need_in_range=False, max_targets=2, reverse=False): """ 带嘲讽等级的目标选择算法 优先级: 攻击范围内最高嘲讽等级 > 同等级最近目标 > 全局最近目标 @@ -70,11 +61,9 @@ def select_targets( # if m.can_be_target() # and m.faction != attacker.faction] # else: - enemies: list[Monster] = [ - m - for m in battlefield.alive_monsters - if m.can_be_target() and m.faction != attacker.faction - ] + enemies: list[Monster] = [m for m in battlefield.alive_monsters + if m.can_be_target() + and m.faction != attacker.faction] # battlefield.query_monster(attacker.position, attacker.attack_range if need_in_range else 9999) # enemies = if not enemies: @@ -87,40 +76,34 @@ def select_targets( in_range = dist <= attacker.attack_range if not need_in_range or (need_in_range and in_range): - enemy_info.append( - { - "enemy": enemy, - "distance": dist, - "aggro": enemy.aggro if in_range else 0, - } - ) + enemy_info.append({ + "enemy": enemy, + "distance": dist, + "aggro": enemy.aggro if in_range else 0 + }) # 按照优先级排序:嘲讽降序 -> 距离升序 if reverse: - sorted_enemies = sorted(enemy_info, key=lambda x: (-x["distance"])) + sorted_enemies = sorted(enemy_info, + key=lambda x: (-x["distance"])) else: - sorted_enemies = sorted( - enemy_info, key=lambda x: (-x["aggro"], x["distance"]) - ) + sorted_enemies = sorted(enemy_info, + key=lambda x: (-x["aggro"], x["distance"])) count = np.minimum(max_targets, len(sorted_enemies)) # 选择前N个目标 return [e["enemy"] for e in sorted_enemies[:count]] @staticmethod - def select_targets_lowest_health( - attacker, battlefield, need_in_range=False, max_targets=2 - ): + def select_targets_lowest_health(attacker, battlefield, need_in_range=False, max_targets=2): """ 带嘲讽等级的目标选择算法 优先级: 攻击范围内最高嘲讽等级 > 同等级最近目标 > 全局最近目标 """ # 获取所有有效敌人 - enemies: list[Monster] = [ - m - for m in battlefield.alive_monsters - if m.can_be_target() and m.faction != attacker.faction - ] + enemies: list[Monster] = [m for m in battlefield.alive_monsters + if m.can_be_target() + and m.faction != attacker.faction] if not enemies: return [] @@ -132,20 +115,16 @@ def select_targets_lowest_health( in_range = dist <= attacker.attack_range if not need_in_range or (need_in_range and in_range): - enemy_info.append( - { - "enemy": enemy, - "distance": dist, - "aggro": enemy.aggro if in_range else 0, - "health_ratio": enemy.health / enemy.max_health, - } - ) + enemy_info.append({ + "enemy": enemy, + "distance": dist, + "aggro": enemy.aggro if in_range else 0, + "health_ratio": enemy.health / enemy.max_health + }) # 按照优先级排序:嘲讽降序 -> 距离升序 - sorted_enemies = sorted( - enemy_info, - key=lambda x: (x["health_ratio"], -x["aggro"], x["distance"]), - ) + sorted_enemies = sorted(enemy_info, + key=lambda x: (x["health_ratio"], -x["aggro"], x["distance"])) count = np.minimum(max_targets, len(sorted_enemies)) # 选择前N个目标 @@ -166,9 +145,7 @@ def apply(self, effect): if effect.type in self.owner.immunity: return # 处理效果叠加逻辑 - existing = next( - (e for e in self.effects if e.type == effect.type), None - ) + existing = next((e for e in self.effects if e.type == effect.type), None) # # 已经冰冻住了就不要施加寒冷效果了 # if effect.type == BuffType.CHILL: @@ -179,18 +156,8 @@ def apply(self, effect): # 寒冷效果叠加就会变成冰冻 if effect.type == BuffType.CHILL: existing.duration = 0 - self.apply( - BuffEffect( - BuffType.FROZEN, - effect.duration, - effect.source, - effect.stacks, - effect.data, - ) - ) - debug_print( - f"{self.owner.name}{self.owner.id} 被 {effect.source.name} 冰冻了!" - ) + self.apply(BuffEffect(BuffType.FROZEN, effect.duration, effect.source, effect.stacks, effect.data)) + debug_print(f"{self.owner.name}{self.owner.id} 被 {effect.source.name} 冰冻了!") return # 其他效果刷新时间 @@ -227,38 +194,25 @@ def _process_dot(self, delta_time): # self.fire_dmg_counter += delta_time # if self.fire_dmg_counter >= 0.33: # self.fire_dmg_counter = 0 - damage = calculate_normal_dmg( - 0, self.owner.magic_resist, 60 * delta_time, DamageType.MAGIC - ) + damage = calculate_normal_dmg(0, self.owner.magic_resist, 60 * delta_time, DamageType.MAGIC) self.owner.take_damage(damage, DamageType.MAGIC) - corrupt = next( - (e for e in self.effects if e.type == BuffType.CORRUPT), None - ) + corrupt = next((e for e in self.effects if e.type == BuffType.CORRUPT), None) if corrupt: # 每秒造成伤害 # self.corrupt_dmg_counter += delta_time # if self.corrupt_dmg_counter >= 1: # self.corrupt_dmg_counter = 0 - damage = calculate_normal_dmg( - 0, self.owner.magic_resist, 100 * delta_time, DamageType.MAGIC - ) + damage = calculate_normal_dmg(0, self.owner.magic_resist, 100 * delta_time, DamageType.MAGIC) self.owner.take_damage(damage, DamageType.MAGIC) - power_stone = next( - (e for e in self.effects if e.type == BuffType.POWER_STONE), None - ) + power_stone = next((e for e in self.effects if e.type == BuffType.POWER_STONE), None) if power_stone: self.power_stay_counter += delta_time - damage = ( - 0.005 - * self.owner.max_health - * self.power_stay_counter - * delta_time - ) + damage = 0.005 * self.owner.max_health * self.power_stay_counter * delta_time if self.owner.take_damage(damage, DamageType.TRUE): pass - # debug_print(f"{self.owner.name}{self.owner.id} 受到了毒圈的{damage}伤害") + #debug_print(f"{self.owner.name}{self.owner.id} 受到了毒圈的{damage}伤害") def _init_effect(self, effect): """初始化效果""" @@ -331,9 +285,7 @@ def __init__(self, data, faction, position, battlefield): self.attack_range = data["攻击范围"]["数值"] self.move_speed = data["移速"]["数值"] self.traits = data["特性"] - self.attack_type = ( - DamageType.PHYSICAL if data["类型"] == "物理" else DamageType.MAGIC - ) + self.attack_type = DamageType.PHYSICAL if data["类型"] == "物理" else DamageType.MAGIC self.char_icon = data.get("符号", "") self.id = -1 self.attack_speed = 100 @@ -349,7 +301,7 @@ def __init__(self, data, faction, position, battlefield): self.frozen = False self.dizzy = False self.invincible = False - self.battlefield: "Battlefield" = battlefield + self.battlefield: 'Battlefield' = battlefield self.status_system = StatusSystem(self) self.element_system = ElementAccumulator(self) self.attack_multiplier = 1 @@ -408,9 +360,7 @@ def increase_skill_cd(self, delta_time): def increase_attack_cd(self, delta_time): """增加攻击技力、攻击频率计算""" - self.attack_time_counter += delta_time * ( - np.maximum(10, np.minimum(self.attack_speed, 600)) / 100 - ) + self.attack_time_counter += delta_time * (np.maximum(10, np.minimum(self.attack_speed, 600)) / 100) def move_toward_enemy(self, delta_time): """根据阵营向对方移动""" @@ -429,9 +379,7 @@ def move_toward_enemy(self, delta_time): # 标准化移动向量并应用速度 norm_direction = direction.normalize() if not self.blocked and self.attack_state == AttackState.等待: - self.velocity = ( - self.velocity * 7 + norm_direction * self.move_speed - ) / 8 + self.velocity = (self.velocity * 7 + norm_direction * self.move_speed) / 8 RADIUS = self.battlefield.HIT_BOX_RADIUS selfRadius = RADIUS * 0.2 if self.blocked else RADIUS @@ -455,12 +403,7 @@ def move_toward_enemy(self, delta_time): depth = selfRadius + radius2 - dist if dist < selfRadius + radius2: # 发生碰撞,挤出 - self.velocity -= ( - dir - * (depth + 0.02) - * hardness1 - / (hardness1 + hardness2) - ) + self.velocity -= dir * (depth + 0.02) * hardness1 / (hardness1 + hardness2) # m.velocity += dir * (depth + 0.02) * hardness2 / (hardness1 + hardness2) def do_move(self, delta_time): @@ -503,10 +446,7 @@ def can_attack(self, delta_time): if self.attack_state == AttackState.前摇: if in_range: self.increase_attack_cd(delta_time) - if ( - self.attack_time_counter - >= self.attack_animation.windup_time - ): + if self.attack_time_counter >= self.attack_animation.windup_time: self.attack_state = AttackState.后摇 return True else: @@ -540,12 +480,8 @@ def update(self, delta_time): self.status_system.update(delta_time) self.update_elemental(delta_time) - if ( - self.target is None - or not self.target.can_be_target() - or (self.target.position - self.position).magnitude - > self.attack_range - ): + if self.target is None or not self.target.can_be_target() or ( + self.target.position - self.position).magnitude > self.attack_range: # 寻找新目标 self.target = self.find_target() @@ -567,9 +503,7 @@ def update(self, delta_time): def find_target(self): """寻找最近的可攻击目标""" - targets = TargetSelector.select_targets( - self, self.battlefield, need_in_range=False, max_targets=1 - ) + targets = TargetSelector.select_targets(self, self.battlefield, need_in_range=False, max_targets=1) if len(targets) > 0: return targets[0] return None @@ -585,21 +519,15 @@ def attack(self, target, gameTime): target.on_hit(self, damage) def apply_damage_to_target(self, target, damage) -> bool: - debug_print( - f"{self.name}{self.id} 对 {target.name}{target.id} 造成{damage}点{self.attack_type}伤害" - ) + debug_print(f"{self.name}{self.id} 对 {target.name}{target.id} 造成{damage}点{self.attack_type}伤害") if target.take_damage(damage, self.attack_type): return True - debug_print( - f"{self.name}{self.id} 没有对 {target.name}{target.id}造成伤害" - ) + debug_print(f"{self.name}{self.id} 没有对 {target.name}{target.id}造成伤害") return False def calculate_damage(self, target, damage): """计算伤害值""" - return calculate_normal_dmg( - target.phy_def, target.magic_resist, damage, self.attack_type - ) + return calculate_normal_dmg(target.phy_def, target.magic_resist, damage, self.attack_type) # if self.attack_type == "物理": # return calculate_normal_dmg(target.phy_def, 0, damage, False) # # return np.maximum(damage - target.phy_def, int(damage * 0.05)) @@ -637,7 +565,6 @@ def on_attack(self, target, damage): target.phy_def = max(0, target.phy_def - 15) debug_print(f"{self.name} 使 {target.name} 防御力降低15") return True - # def apply_damage_to_target(self, target, damage): # if super().apply_damage_to_target(target, damage): # # 实现减防特性 @@ -656,17 +583,8 @@ def on_death(self): debug_print(f"{self.name} 即将自爆!") self.battlefield.projectiles_manager.spawn_projectile( - AOE炸弹( - 0.2, - self.get_attack_power() * 4, - DamageType.PHYSICAL, - self, - self.position, - name="源石虫爆炸", - aoeType=AOEType.Circle, - radius=1.25, - ) - ) + AOE炸弹(0.2, self.get_attack_power() * 4, DamageType.PHYSICAL, self, self.position, name="源石虫爆炸", + aoeType=AOEType.Circle, radius=1.25)) # for m in self.battlefield.monsters: # if m.faction != self.faction and m.is_alive: # distance = np.linalg.norm(m.position - self.position) @@ -682,9 +600,7 @@ class 炽焰源石虫(Monster): def apply_damage_to_target(self, target: Monster, damage): if super().apply_damage_to_target(target, damage): - target.element_system.accumulate( - ElementType.FIRE, self.get_attack_power() - ) + target.element_system.accumulate(ElementType.FIRE, self.get_attack_power()) return True return False @@ -704,7 +620,9 @@ def on_death(self): m.take_damage(dmg, self.attack_type) # 施加10秒寒冷效果 chill = BuffEffect( - type=BuffType.CHILL, duration=10, source=self + type=BuffType.CHILL, + duration=10, + source=self ) m.status_system.apply(chill) debug_print(f"{m.name} 受到{dmg}点爆炸伤害") @@ -721,7 +639,11 @@ def on_hit(self, attacker, damage): super().on_hit(attacker, damage) # 触发加速特性 if self.is_alive and self.speed_boost_counter <= 0: - speed = BuffEffect(type=BuffType.SPEEDUP, duration=2, source=self) + speed = BuffEffect( + type=BuffType.SPEEDUP, + duration=2, + source=self + ) self.status_system.apply(speed) self.speed_boost_counter = 7.0 debug_print(f"{self.name} 进入极速状态!") @@ -756,14 +678,10 @@ def on_death(self): def spawn_small(self): debug_print(f"{self.name} 释放小喷蛛") - self.battlefield.append_monster_name( - "小喷蛛", - self.faction, - self.position - + FastVector( - random.uniform(-1, 1) * 0.2, random.uniform(-1, 1) * 0.2 - ), - ) + self.battlefield.append_monster_name("小喷蛛", self.faction, self.position + FastVector( + random.uniform(-1, 1) * 0.2, + random.uniform(-1, 1) * 0.2 + )) class 提亚卡乌好战者(Monster): @@ -874,10 +792,7 @@ def attack(self, target, gameTime): debug_print(f"{self.name} 进入防御模式") def on_extra_update(self, delta_time): - if ( - self.stage == 1 - and self.battlefield.gameTime - self.last_attack_time >= 20.0 - ): + if self.stage == 1 and self.battlefield.gameTime - self.last_attack_time >= 20.0: self.phy_def -= 300 self.move_speed = self.original_speed self.defenseMode = False @@ -915,9 +830,7 @@ def calculate_damage(self, target, damage): target_def = target.phy_def if self.attack_count >= 4: target_def = target_def * 0.4 - return calculate_normal_dmg( - target_def, target.magic_resist, damage, DamageType.PHYSICAL - ) + return calculate_normal_dmg(target_def, target.magic_resist, damage, DamageType.PHYSICAL) class 高塔术师(Monster): @@ -927,36 +840,22 @@ def on_spawn(self): self.attack_animation = AttackAnimation(0.07, 0.13, 0.8, self) def attack(self, target, gameTime): - targets = TargetSelector.select_targets( - self, self.battlefield, need_in_range=True, max_targets=2 - ) + targets = TargetSelector.select_targets(self, self.battlefield, need_in_range=True, max_targets=2) if len(targets) == 0: return for t in targets: self.battlefield.projectiles_manager.spawn_projectile( - AOE炸弹锁定( - 0.1, - self.get_attack_power(), - DamageType.MAGIC, - self, - t, - name="爆裂魔法", - aoeType=AOEType.Grid8, - ) - ) + AOE炸弹锁定(0.1, self.get_attack_power(), DamageType.MAGIC, self, t, name="爆裂魔法", + aoeType=AOEType.Grid8)) debug_print(f"{self.name}{self.id} 射出爆裂魔法") def get_aoe_targets(self, target): - aoe_targets = [ - m - for m in self.battlefield.monsters - if m.is_alive - and m.faction != self.faction - and abs(m.position.x - target.position.x) <= 1 - and abs(m.position.y - target.position.y) <= 1 - ] + aoe_targets = [m for m in self.battlefield.monsters + if m.is_alive and m.faction != self.faction + and abs(m.position.x - target.position.x) <= 1 + and abs(m.position.y - target.position.y) <= 1] return aoe_targets @@ -985,16 +884,16 @@ def apply_damage_to_target(self, target, damage): if self.attack_count % 3 == 0: # 施加寒冷效果 chill = BuffEffect( - type=BuffType.CHILL, duration=5, source=self + type=BuffType.CHILL, + duration=5, + source=self ) target.status_system.apply(chill) return True return False def attack(self, target, gameTime): - targets = TargetSelector.select_targets( - self, self.battlefield, need_in_range=True, max_targets=2 - ) + targets = TargetSelector.select_targets(self, self.battlefield, need_in_range=True, max_targets=2) if len(targets) == 0: return @@ -1017,9 +916,7 @@ def on_hit(self, attacker, damage): damage = self.calculate_damage(attacker, 300) if self.apply_damage_to_target(attacker, damage): attacker.on_hit(self, damage) - debug_print( - f"{self.name}{self.id} 对 {attacker.name}{attacker.id} 造成{damage}伤害" - ) + debug_print(f"{self.name}{self.id} 对 {attacker.name}{attacker.id} 造成{damage}伤害") class 庞贝(Monster): @@ -1040,9 +937,8 @@ def get_max_skill_bar(self): return 10 def attack(self, target, gameTime): - targets: list[Monster] = TargetSelector.select_targets( - self, self.battlefield, need_in_range=True, max_targets=4 - ) + targets: list[Monster] = TargetSelector.select_targets(self, self.battlefield, need_in_range=True, + max_targets=4) if len(targets) == 0: return @@ -1050,9 +946,11 @@ def attack(self, target, gameTime): damage = self.calculate_damage(m, self.get_attack_power()) if self.apply_damage_to_target(m, damage): m.on_hit(self, damage) - m.status_system.apply( - BuffEffect(type=BuffType.FIRE, duration=10, source=self) - ) + m.status_system.apply(BuffEffect( + type=BuffType.FIRE, + duration=10, + source=self + )) def on_extra_update(self, delta_time): if not self.rage_mode and self.health < 0.5 * self.max_health: @@ -1060,19 +958,10 @@ def on_extra_update(self, delta_time): self.attack_speed += 40 debug_print(f"{self.name} 进入狂暴模式") self.ring_attack_counter += delta_time - targets = TargetSelector.select_targets( - self, self.battlefield, need_in_range=False, max_targets=9999 - ) - if ( - len(targets) > 0 - and (targets[0].position - self.position).magnitude < 0.8 - ): + targets = TargetSelector.select_targets(self, self.battlefield, need_in_range=False, max_targets=9999) + if len(targets) > 0 and (targets[0].position - self.position).magnitude < 0.8: if self.ring_attack_counter >= 10.0: - targets = [ - t - for t in targets - if (t.position - self.position).magnitude < 1.4 - ] + targets = [t for t in targets if (t.position - self.position).magnitude < 1.4] for tar in targets: dmg = self.calculate_damage(tar, 1000) if self.apply_damage_to_target(tar, dmg): @@ -1084,9 +973,11 @@ class 食腐狗(Monster): """食腐狗""" def on_attack(self, target, damage): - target.status_system.apply( - BuffEffect(type=BuffType.CORRUPT, duration=10, source=self) - ) + target.status_system.apply(BuffEffect( + type=BuffType.CORRUPT, + duration=10, + source=self + )) class 鼠鼠(Monster): @@ -1099,7 +990,11 @@ def on_hit(self, attacker, damage): super().on_hit(attacker, damage) # 触发加速特性 if self.is_alive and self.speed_boost_counter <= 0: - speed = BuffEffect(type=BuffType.SPEEDUP, duration=5, source=self) + speed = BuffEffect( + type=BuffType.SPEEDUP, + duration=5, + source=self + ) self.status_system.apply(speed) self.speed_boost_counter = 15.0 debug_print(f"{self.name}{self.id} 进入极速状态!") @@ -1118,23 +1013,14 @@ def on_spawn(self): def on_extra_update(self, delta_time): if self.first_attack: - targets: list[Monster] = TargetSelector.select_targets( - self, self.battlefield, need_in_range=True, max_targets=1 - ) + targets: list[Monster] = TargetSelector.select_targets(self, self.battlefield, need_in_range=True, + max_targets=1) if len(targets) == 0: return self.battlefield.projectiles_manager.spawn_projectile( - AOE炸弹锁定( - 0.2, - self.get_attack_power() * 1.5, - DamageType.MAGIC, - self, - targets[0], - name="“投石机”", - aoeType=AOEType.Grid4, - ) - ) + AOE炸弹锁定(0.2, self.get_attack_power() * 1.5, DamageType.MAGIC, self, targets[0], name="“投石机”", + aoeType=AOEType.Grid4)) self.attack_range = 0.8 self.first_attack = False debug_print(f"{self.name}{self.id} 投掷雪球") @@ -1163,12 +1049,12 @@ def apply_damage_to_target(self, target, damage): # 每第四下攻击会眩晕对面7秒 if self.attack_count % 4 == 0: dizzy = BuffEffect( - type=BuffType.DIZZY, duration=7, source=self + type=BuffType.DIZZY, + duration=7, + source=self ) target.status_system.apply(dizzy) - debug_print( - f"{self.name}{self.id} 眩晕了 {target.name}{target.id}" - ) + debug_print(f"{self.name}{self.id} 眩晕了 {target.name}{target.id}") return True return False @@ -1198,9 +1084,8 @@ def attack(self, target, gameTime): if self.stage == 0: self.on_attack(target, 0) if self.attack_count % 4 == 0: - targets: list[Monster] = TargetSelector.select_targets( - self, self.battlefield, need_in_range=True, max_targets=2 - ) + targets: list[Monster] = TargetSelector.select_targets(self, self.battlefield, need_in_range=True, + max_targets=2) if len(targets) == 0: return @@ -1208,11 +1093,11 @@ def attack(self, target, gameTime): damage = self.calculate_damage(m, self.get_attack_power()) if self.apply_damage_to_target(m, damage): m.on_hit(self, damage) - m.status_system.apply( - BuffEffect( - type=BuffType.DIZZY, duration=3, source=self - ) - ) + m.status_system.apply(BuffEffect( + type=BuffType.DIZZY, + duration=3, + source=self + )) else: super().attack(target, gameTime) return @@ -1229,9 +1114,7 @@ def attack(self, target, gameTime): def calculate_damage(self, target: Monster, damage): if self.stage == 1 and self.attack_count % 4 == 0: target_def = target.phy_def * 0.4 - return calculate_normal_dmg( - target_def, target.magic_resist, damage, DamageType.PHYSICAL - ) + return calculate_normal_dmg(target_def, target.magic_resist, damage, DamageType.PHYSICAL) return super().calculate_damage(target, damage) def on_death(self): @@ -1251,9 +1134,15 @@ def on_death(self): self.health = self.max_health self.status_system.reset() switch_stage = BuffEffect( - type=BuffType.INVINCIBLE2, duration=4, source=self + type=BuffType.INVINCIBLE2, + duration=4, + source=self + ) + dizzy = BuffEffect( + type=BuffType.DIZZY, + duration=4, + source=self ) - dizzy = BuffEffect(type=BuffType.DIZZY, duration=4, source=self) # 转阶段 self.status_system.apply(switch_stage) self.status_system.apply(dizzy) @@ -1324,10 +1213,7 @@ def on_extra_update(self, delta_time): if self.stage == 1: # 蓄力5秒后造成攻击力200%法术伤害 - if ( - self.charging_counter >= 5 - or not self.locked_target.can_be_target() - ): + if self.charging_counter >= 5 or not self.locked_target.can_be_target(): self.stage = 2 self.move_speed = self.original_move_speed self.skill_counter = 0 @@ -1339,9 +1225,7 @@ def on_extra_update(self, delta_time): debug_print(f"{self.name}{self.id} 退出蓄力") if self.locked_target.can_be_target(): - damage = self.calculate_damage( - self.locked_target, self.get_attack_power() * 2 - ) + damage = self.calculate_damage(self.locked_target, self.get_attack_power() * 2) self.on_attack(self.locked_target, damage) if self.apply_damage_to_target(self.locked_target, damage): self.locked_target.on_hit(self, damage) @@ -1397,12 +1281,10 @@ def on_extra_update(self, delta_time): self.stage = 1 self.move_speed = 0 self.charging_counter = 0 - # self.target_pos = self.target.position - self.target_pos = FastVector( - self.target.position.x, self.target.position.y - ) + #self.target_pos = self.target.position + self.target_pos = FastVector(self.target.position.x, self.target.position.y) debug_print(f"{self.name}{self.id} 开始蓄力") - # debug_print(f"{self.name}{self.id} 锁定的坐标是{self.target_pos.x},{self.target_pos.y}") + #debug_print(f"{self.name}{self.id} 锁定的坐标是{self.target_pos.x},{self.target_pos.y}") if self.stage == 1: # 蓄力7秒后造成攻击力250%法术伤害 if self.charging_counter >= 7: @@ -1410,21 +1292,15 @@ def on_extra_update(self, delta_time): self.move_speed = self.original_move_speed self.skill_counter = 0 self.charging_counter = 0 - # debug_print(f"{self.name}{self.id} 轰炸的中心是{self.target_pos.x},{self.target_pos.y}") + #debug_print(f"{self.name}{self.id} 轰炸的中心是{self.target_pos.x},{self.target_pos.y}") for m in self.battlefield.monsters: if m.faction != self.faction and m.can_be_target(): - # 改为max(5|x|,|y|)<=2.5∪max(|x|,5|y|)<=2.5∪max(|x|,|y|)<=1.5 - x = int( - math.floor(m.position.x - self.target_pos.x + 0.5) - ) - y = int( - math.floor(m.position.y - self.target_pos.y + 0.5) - ) + #改为max(5|x|,|y|)<=2.5∪max(|x|,5|y|)<=2.5∪max(|x|,|y|)<=1.5 + x = int(math.floor(m.position.x - self.target_pos.x + 0.5)) + y = int(math.floor(m.position.y - self.target_pos.y + 0.5)) if abs(x) + abs(y) <= 2: - dmg = self.calculate_damage( - m, self.get_attack_power() * 2.5 - ) + dmg = self.calculate_damage(m, self.get_attack_power() * 2.5) if self.apply_damage_to_target(m, dmg): m.on_hit(self, dmg) @@ -1522,31 +1398,19 @@ def get_hit_enemies(self): # 列坐标匹配(同一垂直方向) if abs(x - self_x) <= 0.5: - if ( - m.position.y > self.position.y - and m.position.y - self.position.y < smallest_up - ): + if m.position.y > self.position.y and m.position.y - self.position.y < smallest_up: smallest_up = m.position.y - self.position.y smallest_up_target = m - if ( - m.position.y < self.position.y - and self.position.y - m.position.y < smallest_down - ): + if m.position.y < self.position.y and self.position.y - m.position.y < smallest_down: smallest_down = self.position.y - m.position.y smallest_down_target = m # 行坐标匹配(同一水平方向) if abs(y - self_y) <= 0.5: - if ( - m.position.x > self.position.x - and m.position.x - self.position.x < smallest_right - ): + if m.position.x > self.position.x and m.position.x - self.position.x < smallest_right: smallest_right = m.position.x - self.position.x smallest_right_target = m - if ( - m.position.x < self.position.x - and self.position.x - m.position.x < smallest_left - ): + if m.position.x < self.position.x and self.position.x - m.position.x < smallest_left: smallest_left = self.position.x - m.position.x smallest_left_target = m @@ -1587,8 +1451,7 @@ def on_spawn(self): class AttackNode: """攻击节点数据类""" - - __slots__ = ["target", "damage_multiplier"] # 优化内存使用 + __slots__ = ['target', 'damage_multiplier'] # 优化内存使用 def __init__(self, target, multiplier): self.target = target @@ -1607,9 +1470,7 @@ def chain_attack(self, initial_target: Monster) -> list[AttackNode]: current_multiplier = 1.0 # 添加初始攻击 - attack_chain.append( - self.AttackNode(current_target, current_multiplier) - ) + attack_chain.append(self.AttackNode(current_target, current_multiplier)) visited.add(current_target.id) # 执行最多4次跳跃 @@ -1617,12 +1478,8 @@ def chain_attack(self, initial_target: Monster) -> list[AttackNode]: # 寻找下一个候选目标 candidates = self._find_candidates( current_target.position, - [ - m - for m in self.battlefield.alive_monsters - if m.can_be_target() and m.faction != self.faction - ], - visited, + [m for m in self.battlefield.alive_monsters if m.can_be_target() and m.faction != self.faction], + visited ) if not candidates: @@ -1641,9 +1498,10 @@ def chain_attack(self, initial_target: Monster) -> list[AttackNode]: return attack_chain - def _find_candidates( - self, origin: FastVector, enemies: List["Monster"], visited: set - ) -> List[tuple]: + def _find_candidates(self, + origin: FastVector, + enemies: List['Monster'], + visited: set) -> List[tuple]: """ 查找有效候选目标 :param origin: 当前攻击源点坐标 (x, y) @@ -1676,25 +1534,25 @@ def attack(self, target, gameTime): node.target.on_hit(self, dmg) # 所有人都有一样的凋亡损伤 t = ElementType.NECRO_RIGHT - node.target.element_system.accumulate( - t, self.get_attack_power() * 0.3 - ) + node.target.element_system.accumulate(t, self.get_attack_power() * 0.3) # debug_print(f"{self.name}{self.id} 对 {node.target.name}{node.target.id} 造成{dmg}点魔法伤害") def on_death(self): debug_print(f"{self.name} 变成大君之赐") - m = self.battlefield.append_monster_name( - "大君之赐", - self.faction, - self.position - + FastVector( - random.uniform(-1, 1) * 0.2, random.uniform(-1, 1) * 0.2 - ), - ) + m = self.battlefield.append_monster_name("大君之赐", self.faction, self.position + FastVector( + random.uniform(-1, 1) * 0.2, + random.uniform(-1, 1) * 0.2 + )) switch_stage = BuffEffect( - type=BuffType.INVINCIBLE2, duration=1, source=self + type=BuffType.INVINCIBLE2, + duration=1, + source=self + ) + dizzy = BuffEffect( + type=BuffType.DIZZY, + duration=1, + source=self ) - dizzy = BuffEffect(type=BuffType.DIZZY, duration=1, source=self) # 转阶段 m.status_system.apply(switch_stage) m.status_system.apply(dizzy) @@ -1722,11 +1580,7 @@ def on_spawn(self): def on_extra_update(self, delta_time): self.decay_timer += delta_time - if ( - self.decay_timer > 3.5 - and abs(round(self.decay_timer - 3.5) - (self.decay_timer - 3.5)) - < 0.001 - ): + if self.decay_timer > 3.5 and abs(round(self.decay_timer - 3.5) - (self.decay_timer - 3.5)) < 0.001: if self.attack_stack > 0: self.attack_stack -= 2 self.attack_multiplier -= 0.3 @@ -1765,23 +1619,14 @@ def on_spawn(self): self.attack_animation = AttackAnimation(0.05, 0.15, 0.8, self) def attack(self, target, gameTime): - targets: list[Monster] = TargetSelector.select_targets( - self, self.battlefield, need_in_range=True, max_targets=1 - ) + targets: list[Monster] = TargetSelector.select_targets(self, self.battlefield, need_in_range=True, + max_targets=1) if len(targets) == 0: return self.battlefield.projectiles_manager.spawn_projectile( - AOE炸弹锁定( - 0.2, - self.get_attack_power(), - self.attack_type, - self, - targets[0], - name="火箭弹", - aoeType=AOEType.Grid8, - ) - ) + AOE炸弹锁定(0.2, self.get_attack_power(), self.attack_type, self, targets[0], name="火箭弹", + aoeType=AOEType.Grid8)) debug_print(f"{self.name}{self.id} 开炮") @@ -1803,16 +1648,8 @@ def on_extra_update(self, delta_time): distance = direction.magnitude if distance <= self.attack_range: self.battlefield.projectiles_manager.spawn_projectile( - AOE炸弹锁定( - 0.2, - self.get_attack_power() * 2, - self.attack_type, - self, - self.target, - name="火箭弹", - aoeType=AOEType.Grid8, - ) - ) + AOE炸弹锁定(0.2, self.get_attack_power() * 2, self.attack_type, self, self.target, + name="火箭弹", aoeType=AOEType.Grid8)) self.stage = 1 debug_print(f"{self.name}{self.id} 射出火箭弹") @@ -1863,9 +1700,7 @@ def get_max_skill_bar(self): return super().get_max_skill_bar() def lock_target(self): - targets = TargetSelector.select_targets( - self, self.battlefield, need_in_range=True, max_targets=1 - ) + targets = TargetSelector.select_targets(self, self.battlefield, need_in_range=True, max_targets=1) if len(targets) > 0: return targets[0] return None @@ -1888,30 +1723,21 @@ def on_extra_update(self, delta_time): self.skill_counter = 0 else: # 法术伤害 - dmg = self.calculate_damage( - self.locked_target, - self.get_attack_power() * 0.4 * delta_time, - ) + dmg = self.calculate_damage(self.locked_target, self.get_attack_power() * 0.4 * delta_time) if self.apply_damage_to_target(self.locked_target, dmg): self.locked_target.on_hit(self, dmg) - # debug_print(f"{self.locked_target.name}{self.locked_target.id} 受到 {self.name}{self.id} 的{dmg}点法术伤害") + # debug_print(f"{self.locked_target.name}{self.locked_target.id} 受到 {self.name}{self.id} 的{dmg}点法术伤害") # 蓄力完成的凋亡损伤 - if ( - self.locked_target.can_be_target() - and self.charging_counter2 >= 8.0 - ): + if self.locked_target.can_be_target() and self.charging_counter2 >= 8.0: for m in self.get_aoe_targets(self.locked_target): dmg = self.get_attack_power() * 2.2 - m.element_system.accumulate( - ElementType.NECRO_RIGHT, dmg - ) - debug_print( - f"{m.name}{m.id} 受到 {self.name}{self.id} 的{dmg}点凋亡损伤" - ) + m.element_system.accumulate(ElementType.NECRO_RIGHT, dmg) + debug_print(f"{m.name}{m.id} 受到 {self.name}{self.id} 的{dmg}点凋亡损伤") self.stage = 0 self.move_speed = self.original_move_speed self.locked_target = None self.charging_counter2 = 0 + def get_aoe_targets(self, target): # 这个是获取|dx|<=1,|dy|<=1范围内的目标,是个矩形 @@ -1922,21 +1748,13 @@ def get_aoe_targets(self, target): # 十字:|dx|≤0.5∪|dy|≤0.5 # 矩形:max(|dx|,|dy|)≤1.5 # 十字与矩形取交集 - aoe_targets = [ - m - for m in self.battlefield.monsters - if m.is_alive - and m.faction != self.faction - and np.maximum( - abs(m.position.x - target.position.x), - abs(m.position.y - target.position.y), - ) - <= 1.5 - and ( - abs(m.position.x - target.position.x) <= 0.5 - or abs(m.position.y - target.position.y) <= 0.5 - ) - ] + aoe_targets = [m for m in self.battlefield.monsters + if m.is_alive + and m.faction != self.faction + and np.maximum(abs(m.position.x - target.position.x), + abs(m.position.y - target.position.y)) <= 1.5 + and (abs(m.position.x - target.position.x) <= 0.5 or abs( + m.position.y - target.position.y) <= 0.5)] return aoe_targets def attack(self, target, gameTime): @@ -1947,9 +1765,7 @@ def attack(self, target, gameTime): self.on_attack(target, damage) if self.apply_damage_to_target(target, damage): t = ElementType.NECRO_RIGHT - target.element_system.accumulate( - t, self.get_attack_power() * 0.25 - ) + target.element_system.accumulate(t, self.get_attack_power() * 0.25) target.on_hit(self, damage) @@ -2011,9 +1827,7 @@ def on_spawn(self): """标枪恐鱼穿刺者""" def attack(self, target, gameTime): - targets = TargetSelector.select_targets_lowest_health( - self, self.battlefield, need_in_range=True, max_targets=1 - ) + targets = TargetSelector.select_targets_lowest_health(self, self.battlefield, need_in_range=True, max_targets=1) if len(targets) == 0: return self.target = targets[0] @@ -2078,9 +1892,7 @@ def attack(self, target: Monster, gameTime): # 丢出酒桶以后 self.move_speed /= 3 self.stage = 1 - self.battlefield.add_new_zone( - WineZone(target.position, self.battlefield, 12, self.faction) - ) + self.battlefield.add_new_zone(WineZone(target.position, self.battlefield, 12, self.faction)) class 复仇者(Monster): @@ -2139,9 +1951,9 @@ def on_extra_update(self, delta_time): def on_death(self): enemies = enemies = [ - m - for m in self.battlefield.monsters - if m.can_be_target() and m.faction != self.faction + m for m in self.battlefield.monsters + if m.can_be_target() + and m.faction != self.faction ] if not enemies: return @@ -2179,7 +1991,9 @@ def on_extra_update(self, delta_time): self.move_speed = self.origial_move_speed * 3 self.speed_up_timer = 0 switch_stage = BuffEffect( - type=BuffType.INVINCIBLE, duration=10, source=self + type=BuffType.INVINCIBLE, + duration=10, + source=self ) self.status_system.apply(switch_stage) @@ -2235,32 +2049,21 @@ def increase_skill_cd(self, delta_time): if self.stage == 0: if self.target: for m in self.get_aoe_targets(self.target): - damage = self.calculate_damage( - m, self.get_attack_power() * 2 - ) + damage = self.calculate_damage(m, self.get_attack_power() * 2) if self.apply_damage_to_target(m, damage): m.on_hit(self, damage) self.skill1_timer = 0 elif self.stage == 2: # 第二形态还会对最远目标释放一次 - targets = TargetSelector.select_targets( - self, - self.battlefield, - need_in_range=False, - max_targets=1, - reverse=True, - ) + targets = TargetSelector.select_targets(self, self.battlefield, need_in_range=False, max_targets=1, + reverse=True) if len(targets) > 0: for m in self.get_aoe_targets(self.target): - damage = self.calculate_damage( - m, self.get_attack_power() * 2 - ) + damage = self.calculate_damage(m, self.get_attack_power() * 2) if self.apply_damage_to_target(m, damage): m.on_hit(self, damage) for m in self.get_aoe_targets(targets[0]): - damage = self.calculate_damage( - m, self.get_attack_power() * 2 - ) + damage = self.calculate_damage(m, self.get_attack_power() * 2) if self.apply_damage_to_target(m, damage): m.on_hit(self, damage) self.skill1_timer = 0 @@ -2274,9 +2077,7 @@ def increase_skill_cd(self, delta_time): if self.shield_timer > 15: if self.shield > 0: for m in self.get_aoe_targets_skill2(): - damage = self.calculate_damage( - m, self.get_attack_power() * 8 - ) + damage = self.calculate_damage(m, self.get_attack_power() * 8) if self.apply_damage_to_target(m, damage): m.on_hit(self, damage) self.shield = 0 @@ -2289,9 +2090,7 @@ def increase_skill_cd(self, delta_time): if self.shield_timer > 15: if self.shield > 0: for m in self.get_aoe_targets_skill2(): - damage = self.calculate_damage( - m, self.get_attack_power() * 12 - ) + damage = self.calculate_damage(m, self.get_attack_power() * 12) if self.apply_damage_to_target(m, damage): m.on_hit(self, damage) self.shield = 0 @@ -2301,24 +2100,15 @@ def increase_skill_cd(self, delta_time): # 十字aoe判定 def get_aoe_targets(self, target): - aoe_targets = [ - m - for m in self.battlefield.monsters - if m.is_alive - and m.faction != self.faction - and abs(m.position.x - target.position.x) <= 2 - and abs(m.position.y - target.position.y) <= 2 - ] + aoe_targets = [m for m in self.battlefield.monsters + if m.is_alive and m.faction != self.faction + and abs(m.position.x - target.position.x) <= 2 and abs(m.position.y - target.position.y) <= 2] return aoe_targets def get_aoe_targets_skill2(self): - aoe_targets = [ - m - for m in self.battlefield.monsters - if m.is_alive - and m.faction != self.faction - and (m.position - self.position).magnitude < 3 - ] + aoe_targets = [m for m in self.battlefield.monsters + if m.is_alive and m.faction != self.faction + and (m.position - self.position).magnitude < 3] return aoe_targets def take_damage(self, damage, attack_type) -> bool: @@ -2394,17 +2184,16 @@ class MonsterFactory: "雷德": 雷德, "自在": 自在, # "扎罗": 扎罗, + # 添加更多映射... - "炮击组长": 炮击组长, + "炮击组长": 炮击组长 } @classmethod def create_monster(cls, data, faction, position, battlefield): monster_type = data["名字"] if monster_type in cls._monster_classes: - m = cls._monster_classes[monster_type]( - data, faction, position, battlefield - ) + m = cls._monster_classes[monster_type](data, faction, position, battlefield) m.on_spawn() return m else: diff --git a/src/simulation/projectiles.py b/simulator/projectiles.py similarity index 52% rename from src/simulation/projectiles.py rename to simulator/projectiles.py index d31a249..15c0882 100644 --- a/src/simulation/projectiles.py +++ b/simulator/projectiles.py @@ -10,18 +10,11 @@ if TYPE_CHECKING: # 仅用于IDE类型提示,不会真实导入 - from .monsters import Monster + from .monsters import Monster from .battle_field import Battlefield - class Projectile: - def __init__( - self, - max_lifetime, - damage: float, - damageType: DamageType, - source: "Monster", - ): + def __init__(self, max_lifetime, damage : float, damageType : DamageType, source : "Monster"): self.lifetime = 0 self.max_lifetime = max_lifetime self.is_alive = True @@ -34,17 +27,9 @@ def update(self, delta_time, battle_field): """需被子类重写""" raise NotImplementedError - # 组件类型实现 class HomingProjectile(Projectile): - def __init__( - self, - max_lifetime, - damage: float, - damageType: DamageType, - source: "Monster", - target_enemy: "Monster", - ): + def __init__(self, max_lifetime, damage : float, damageType : DamageType, source : "Monster", target_enemy: "Monster"): super().__init__(max_lifetime, damage, damageType, source) self.target = target_enemy # 敌人对象引用 @@ -52,26 +37,18 @@ def update(self, delta_time, battle_field): if not self.target.is_alive: self.is_alive = False return - + self.lifetime += delta_time if self.lifetime >= self.max_lifetime: self.on_timeout(battle_field) self.is_alive = False - + def on_timeout(self, battle_field): """需被子类重写""" raise NotImplementedError - class TimedProjectile(Projectile): - def __init__( - self, - max_lifetime, - damage: float, - damageType: DamageType, - source: "Monster", - target_position, - ): + def __init__(self, max_lifetime, damage : float, damageType : DamageType, source : "Monster", target_position): super().__init__(max_lifetime, damage, damageType, source) self.target_pos = FastVector(target_position.x, target_position.y) @@ -87,12 +64,12 @@ def on_impact(self, battle_field): class ProjectileManager: - def __init__(self, battle_field: "Battlefield"): + def __init__(self, battle_field : 'Battlefield'): self.projectiles = [] self.global_id_counter = 0 self.battle_field = battle_field - def spawn_projectile(self, projectile: Projectile): + def spawn_projectile(self, projectile : Projectile): """使用对象池创建射弹""" self.projectiles.append(projectile) projectile.id = self.global_id_counter @@ -111,114 +88,61 @@ class AOEType(Enum): Grid8 = "八格" Circle = "圆形" - -def get_aoe_targets(source, target_pos, battle_field: "Battlefield"): +def get_aoe_targets(source, target_pos, battle_field: 'Battlefield'): if source.aoe_Type == AOEType.Grid8: - aoe_targets = [ - m - for m in battle_field.alive_monsters - if m.is_alive - and m.faction != source.source.faction - and np.maximum( - abs(m.position.x - target_pos.x), - abs(m.position.y - target_pos.y), - ) - <= 1 - ] + aoe_targets = [m for m in battle_field.alive_monsters + if m.is_alive and m.faction != source.source.faction + and np.maximum(abs(m.position.x - target_pos.x), abs(m.position.y - target_pos.y)) <= 1] elif source.aoe_Type == AOEType.Grid4: - aoe_targets = [ - m - for m in battle_field.alive_monsters - if m.is_alive - and m.faction != source.source.faction - and abs(int(math.floor(m.position.x - target_pos.x + 0.5))) - + abs(int(math.floor(m.position.y - target_pos.y + 0.5))) - <= 1 - ] + aoe_targets = [m for m in battle_field.alive_monsters + if m.is_alive and m.faction != source.source.faction + and abs(int(math.floor(m.position.x - target_pos.x + 0.5))) + abs(int(math.floor(m.position.y - target_pos.y + 0.5))) <= 1] elif source.aoe_Type == AOEType.Circle: - aoe_targets = [ - m - for m in battle_field.query_monster(target_pos, source.radius) - if m.is_alive and m.faction != source.source.faction - ] + aoe_targets = [m for m in battle_field.query_monster(target_pos, source.radius) + if m.is_alive and m.faction != source.source.faction] return aoe_targets - class AOE炸弹(TimedProjectile): - def __init__( - self, - max_lifetime, - damage: float, - damageType: DamageType, - source: "Monster", - target_position, - name: str, - aoeType: AOEType, - radius=1, - ): - super().__init__( - max_lifetime, damage, damageType, source, target_position - ) + def __init__(self, max_lifetime, damage : float, damageType : DamageType, source : "Monster", target_position, name : str, aoeType : AOEType, radius=1): + super().__init__(max_lifetime, damage, damageType, source, target_position) self.name = name self.aoe_Type = aoeType self.radius = radius - def apply_damage_to_target(self, m: "Monster", damage): - debug_print( - f"{self.source.name}{self.source.id} 的{self.name}对 {m.name}{m.id} 造成{damage}点{self.damage_type}伤害" - ) + def apply_damage_to_target(self, m : 'Monster', damage): + debug_print(f"{self.source.name}{self.source.id} 的{self.name}对 {m.name}{m.id} 造成{damage}点{self.damage_type}伤害") if m.take_damage(damage, self.damage_type): return True - debug_print( - f"{self.source.name}{self.source.id} 的{self.name}没有对 {m.name}{m.id}造成伤害" - ) + debug_print(f"{self.source.name}{self.source.id} 的{self.name}没有对 {m.name}{m.id}造成伤害") return False - def on_impact(self, battle_field: "Battlefield"): + def on_impact(self, battle_field:'Battlefield'): aoe_targets = get_aoe_targets(self, self.target_pos, battle_field) for m in aoe_targets: - damage = calculate_normal_dmg( - m.phy_def, m.magic_resist, self.damage, self.damage_type - ) + damage = calculate_normal_dmg(m.phy_def, m.magic_resist, self.damage, self.damage_type) if self.apply_damage_to_target(m, damage): m.on_hit(self.source, damage) - + class AOE炸弹锁定(HomingProjectile): - def __init__( - self, - max_lifetime, - damage: float, - damageType: DamageType, - source: "Monster", - target: "Monster", - name: str, - aoeType: AOEType, - radius=1, - ): + def __init__(self, max_lifetime, damage : float, damageType : DamageType, source : "Monster", target : 'Monster', name : str, aoeType : AOEType, radius=1): super().__init__(max_lifetime, damage, damageType, source, target) self.name = name self.aoe_Type = aoeType self.radius = radius - def apply_damage_to_target(self, m: "Monster", damage): - debug_print( - f"{self.source.name}{self.source.id} 的{self.name}对 {m.name}{m.id} 造成{damage}点{self.damage_type}伤害" - ) + def apply_damage_to_target(self, m : 'Monster', damage): + debug_print(f"{self.source.name}{self.source.id} 的{self.name}对 {m.name}{m.id} 造成{damage}点{self.damage_type}伤害") if m.take_damage(damage, self.damage_type): return True - debug_print( - f"{self.source.name}{self.source.id} 的{self.name}没有对 {m.name}{m.id}造成伤害" - ) + debug_print(f"{self.source.name}{self.source.id} 的{self.name}没有对 {m.name}{m.id}造成伤害") return False - def on_timeout(self, battle_field: "Battlefield"): + def on_timeout(self, battle_field:'Battlefield'): # if not self.target.can_be_target(): # return aoe_targets = get_aoe_targets(self, self.target.position, battle_field) for m in aoe_targets: - damage = calculate_normal_dmg( - m.phy_def, m.magic_resist, self.damage, self.damage_type - ) + damage = calculate_normal_dmg(m.phy_def, m.magic_resist, self.damage, self.damage_type) if self.apply_damage_to_target(m, damage): m.on_hit(self.source, damage) diff --git a/simulator/scene.json b/simulator/scene.json new file mode 100644 index 0000000..39a51eb --- /dev/null +++ b/simulator/scene.json @@ -0,0 +1,58 @@ +{ + "left": + [ + { "name": "酸液源石虫·α", "count": 0 }, + { "name": "高能源石虫", "count": 0 }, + { "name": "阿咬", "count": 0 }, + { "name": "狂暴的猎狗pro", "count": 0 }, + { "name": "提亚卡乌好战者", "count": 0 }, + { "name": "污染躯壳", "count": 0 }, + { "name": "大盾哥", "count": 0 }, + { "name": "宿主流浪者", "count": 0 }, + { "name": "光剑", "count": 4 }, + { "name": "泥岩巨像", "count": 0 }, + { "name": "呼啸骑士团学徒", "count": 0 }, + { "name": "萨卡兹大剑手", "count": 0 }, + { "name": "狂暴宿主组长", "count": 0 }, + { "name": "海螺", "count": 0 }, + { "name": "拳手囚犯", "count": 0 }, + { "name": "高塔术师", "count": 0 }, + { "name": "冰原术师", "count": 0 }, + { "name": "矿脉守卫", "count": 0 }, + { "name": "“庞贝”", "count": 0 }, + { "name": "绵羊", "count": 0 }, + { "name": "食腐狗", "count": 0 }, + { "name": "温顺的武装驮兽", "count": 3 }, + { "name": "鼠鼠", "count": 4 }, + { "name": "“投石机”", "count": 0 }, + { "name": "船长", "count": 0 } + ], + "right": + [ + { "name": "酸液源石虫·α", "count": 0 }, + { "name": "高能源石虫", "count": 0 }, + { "name": "阿咬", "count": 0 }, + { "name": "狂暴的猎狗pro", "count": 0 }, + { "name": "提亚卡乌好战者", "count": 0 }, + { "name": "污染躯壳", "count": 0 }, + { "name": "大盾哥", "count": 0 }, + { "name": "宿主流浪者", "count": 0 }, + { "name": "光剑", "count": 0 }, + { "name": "泥岩巨像", "count": 0 }, + { "name": "呼啸骑士团学徒", "count": 0 }, + { "name": "萨卡兹大剑手", "count": 0 }, + { "name": "狂暴宿主组长", "count": 0 }, + { "name": "海螺", "count": 0 }, + { "name": "拳手囚犯", "count": 11 }, + { "name": "高塔术师", "count": 0 }, + { "name": "冰原术师", "count": 0 }, + { "name": "矿脉守卫", "count": 0 }, + { "name": "“庞贝”", "count": 3 }, + { "name": "绵羊", "count": 9 }, + { "name": "食腐狗", "count": 0 }, + { "name": "温顺的武装驮兽", "count": 0 }, + { "name": "鼠鼠", "count": 0 }, + { "name": "“投石机”", "count": 0 }, + { "name": "船长", "count": 0 } + ] +} \ No newline at end of file diff --git a/src/simulation/simulate.py b/simulator/simulate.py similarity index 60% rename from src/simulation/simulate.py rename to simulator/simulate.py index fd38d9c..8b3a58b 100644 --- a/src/simulation/simulate.py +++ b/simulator/simulate.py @@ -1,4 +1,9 @@ import json +import math +import random +import time +from enum import Enum +import numpy as np import pandas as pd from tqdm import tqdm @@ -14,82 +19,66 @@ def process_battle_data(csv_path): """ # 读取CSV文件(假设没有表头) df = pd.read_csv(csv_path, header=1) - + # 数据结构化处理 battle_records = [] - + for _, row in df.iterrows(): # 分解左右阵营数据 - left_data = row[0:56] # 1-56列 (0-based索引0-55) + left_data = row[0:56] # 1-56列 (0-based索引0-55) right_data = row[56:112] # 56-112列 (0-based索引56-111) - winner = row[112] # 69列 (0-based索引112) - + winner = row[112] # 69列 (0-based索引112) + # 构建阵营字典(ID从1开始) - left_army = { - MONSTER_MAPPING[i]: int(count) - for i, count in enumerate(left_data) - if count > 0 - } - right_army = { - MONSTER_MAPPING[i]: int(count) - for i, count in enumerate(right_data) - if count > 0 - } - + left_army = {MONSTER_MAPPING[i]: int(count) for i, count in enumerate(left_data) if count > 0} + right_army = {MONSTER_MAPPING[i]: int(count) for i, count in enumerate(right_data) if count > 0} + # 构建记录格式 battle_record = { "left": left_army, "right": right_army, - "result": "left" if winner == "L" else "right", + "result": "left" if winner == 'L' else "right" } - + battle_records.append(battle_record) - + return battle_records - def main(): """主函数""" # 加载怪物数据 - with open("arknight/monsters.json", encoding="utf-8") as f: + with open("arknight/monsters.json", encoding='utf-8') as f: monster_data = json.load(f)["monsters"] - + # with open("scene.json", encoding='utf-8') as f: # scene_config = json.load(f) # 使用示例,直接修改这里的csv文件就可以跑模拟 battle_data = process_battle_data("arknight/56fin2_66k.csv") + win = 0 matches = 0 for scene_config in tqdm(battle_data): if VISUALIZATION_MODE: - scene_config = { - "left": {"宿主流浪者": 7, "污染躯壳": 14, "凋零萨卡兹": 5}, - "right": {"大喷蛛": 4, "杰斯顿·威廉姆斯": 1, "衣架": 10}, - "result": "right", - } + scene_config = {"left": {"宿主流浪者": 7, "污染躯壳": 14, "凋零萨卡兹": 5}, "right": {"大喷蛛": 4, "杰斯顿·威廉姆斯": 1, "衣架": 10}, "result": "right"} + - # { "left": { "护盾哥": 5, "污染躯壳": 11, "船长": 5 }, "right": { "炮击组长": 4, "沸血骑士团精锐": 4, "雪境精锐": 4}, "result": "left" } + #{ "left": { "护盾哥": 5, "污染躯壳": 11, "船长": 5 }, "right": { "炮击组长": 4, "沸血骑士团精锐": 4, "雪境精锐": 4}, "result": "left" } # 用户配置 left_army = scene_config["left"] right_army = scene_config["right"] - + # 初始化战场 leftWins = 0 for i in range(3): battlefield = Battlefield(monster_data) - if not battlefield.setup_battle( - left_army, right_army, monster_data - ): + if not battlefield.setup_battle(left_army, right_army, monster_data): continue - + # 开始战斗 - if ( - battlefield.run_battle(visualize=VISUALIZATION_MODE) - == Faction.LEFT - ): + if battlefield.run_battle(visualize=VISUALIZATION_MODE) == Faction.LEFT: leftWins += 1 if leftWins >= 2: break @@ -97,15 +86,14 @@ def main(): break left_win = leftWins >= 2 - - if (left_win and scene_config["result"] == "left") or ( - not left_win and scene_config["result"] == "right" - ): + + if (left_win and scene_config["result"] == "left") or (not left_win and scene_config["result"] == "right"): win += 1 else: - with open("errors.json", encoding="utf-8", mode="+a") as f: + with open("errors.json", encoding='utf-8', mode='+a') as f: f.write(json.dumps(scene_config, ensure_ascii=False)) - f.write("\n") + f.write('\n') + matches += 1 print(f"当前胜率:{win} / {matches}") diff --git a/simulator/stats.py b/simulator/stats.py new file mode 100644 index 0000000..95becc6 --- /dev/null +++ b/simulator/stats.py @@ -0,0 +1,172 @@ +import json +from collections import defaultdict +import numpy as np +import pandas as pd +from scipy.stats import fisher_exact +import matplotlib.pyplot as plt + +# 配置中文字体 +plt.rcParams['font.sans-serif'] = ['Microsoft YaHei'] +plt.rcParams['axes.unicode_minus'] = False + + +def calculate_significance(row): + """计算修正后的统计显著性""" + # 关键参数 + error_total = row['under_estimate'] + row['over_estimate'] # 错误样本总次数 + correct_total = row['total'] - error_total # 正确样本总次数 + + # 过滤无效数据 + if error_total == 0 or correct_total == 0: + return np.nan + + # 低估检验(单侧) + contingency_under = [ + [row['under_estimate'], row['over_estimate']], + [0, correct_total] + ] + _, under_p = fisher_exact(contingency_under, alternative='greater') + + # 高估检验(单侧) + contingency_over = [ + [row['over_estimate'], row['under_estimate']], + [0, correct_total] + ] + _, over_p = fisher_exact(contingency_over, alternative='greater') + + return under_p + over_p + +def analyze_monster_balance(data_path): + # 初始化统计容器 + stats = defaultdict(lambda: { + 'total': 0, # 总出现次数 + 'error': 0, # 错误预测中出现次数 + 'under_estimate': 0, # 低估计数(实际应胜但预测失败) + 'over_estimate': 0 # 高估计数(实际应败但预测成功) + }) + + # 数据加载与分析 + with open(data_path, encoding='utf-8') as f: + for line in f: + record = json.loads(line.strip()) + actual = record['result'] + pred = record['pred'] + + for side in ["left", "right"]: + for monster, count in record[side].items(): + # 基础统计 + stats[monster]['total'] += 1 + + # 仅统计预测错误的情况 + if pred != actual: + stats[monster]['error'] += 1 + + # 判断估计方向 + if (side == actual): + stats[monster]['under_estimate'] += 1 + elif (side != actual): + stats[monster]['over_estimate'] += 1 + + # 构建分析数据框 + df = pd.DataFrame.from_dict(stats, orient='index') + df = df[df['total'] > 0] # 过滤无效数据 + + # 计算统计指标 + df['error_rate'] = df['error'] / df['total'] + df['bias_score'] = (df['under_estimate'] - df['over_estimate']) / df['total'] + df['abs_bias'] = abs(df['bias_score']) + + # # Fisher精确检验 + # def apply_fisher(row): + # contingency = [ + # [row['under_estimate'], row['over_estimate']], + # [row['total'] - row['under_estimate'], + # row['total'] - row['over_estimate']] + # ] + # _, p_value = fisher_exact(contingency) + # return p_value + + df['p_value'] = df.apply(calculate_significance, axis=1) + + # 筛选显著结果 (p<0.04 且总出现次数>1) + valid = df[(df['p_value'] < 0.04) & (df['total'] >= 1)] + + # 生成可视化 + plot_monster_bias(valid) + + return valid.sort_values('abs_bias', ascending=False) + +def plot_monster_bias(df): + plt.figure(figsize=(12, 8)) + + # 设置颜色映射 + colors = np.where(df['bias_score'] > 0, + '#4C72B0', # 蓝色表示低估 + '#DD8452') # 橙色表示高估 + + # 绘制条形图 + bars = plt.barh(df.index, df['bias_score'], color=colors) + + # 添加统计标注 + for bar, (_, row) in zip(bars, df.iterrows()): + plt.text(bar.get_width() + 0.02, + bar.get_y() + bar.get_height()/2, + f"p={row['p_value']:.3f}\nN={row['total']}", + va='center') + + # 图表装饰 + plt.axvline(0, color='gray', linestyle='--') + plt.title('怪物数值平衡性分析', fontsize=14) + plt.xlabel('偏差分数(正值表示低估,负值表示高估)') + plt.ylabel('怪物名称') + plt.xlim(-1.1, 1.1) + plt.grid(axis='x', alpha=0.3) + plt.tight_layout() + plt.savefig('monster_balance.png', dpi=300) + plt.close() + +# 执行分析 +if __name__ == "__main__": + result_df = analyze_monster_balance("../errors.json") + + # 打印格式化结果 + print("="*60) + print(f"{'怪物名称':<10}\t{'偏差方向':<8}\t{'偏差分数':<8}\t{'p值':<10}\t{'样本量'}") + print("-"*60) + for name, row in result_df.iterrows(): + direction = "低估" if row['bias_score'] > 0 else "高估" + print(f"{name:<10}\t{direction:<8}\t{row['bias_score']:.2f}{'*' if row['p_value']<0.01 else '':<4}" + f"\t{row['p_value']:.3f}\t{'':<6}\t{row['total']}") + + plot_monster_bias(result_df) + +with open("../errors.json", encoding='utf-8') as f: + data = f.read() + +counter = defaultdict(int) +power_counter = defaultdict(int) +err_counter = defaultdict(int) + +for line in data.strip().split('\n'): + record = json.loads(line) + result = record['result'] + pred = record['pred'] + for side in ["left", "right"]: + for monster, count in record[side].items(): + counter[monster] += 1 + if pred != result: + if side == result: + # 应该获胜却没有获胜,有低估的可能性 + power_counter[monster] += 1 + else: + # 不该获胜的获胜了,有高估的可能性 + power_counter[monster] -= 1 + err_counter[monster] += 1 + +sorted_monsters = sorted(err_counter.items(), key=lambda x: (-x[1] / counter[x[0]], x[0])) + +for monster, count in sorted_monsters: + str = "低估" if power_counter[monster] > 0 else "高估" + total = counter[monster] + err_total = count + print(f"{monster}: {err_total}/{total} = {err_total/total} 估计值:【{str}】{-power_counter[monster] / total * 100}") \ No newline at end of file diff --git a/src/simulation/utils.py b/simulator/utils.py similarity index 80% rename from src/simulation/utils.py rename to simulator/utils.py index d6d2ec6..d078175 100644 --- a/src/simulation/utils.py +++ b/simulator/utils.py @@ -1,9 +1,11 @@ + + from collections import defaultdict from dataclasses import dataclass, field from enum import Enum import logging import math -import csv # 添加csv模块导入 +import csv # 添加csv模块导入 import numpy as np @@ -19,17 +21,14 @@ VISUALIZATION_MODE = True - def debug_print(msg): if VISUALIZATION_MODE: print(msg) - class Faction(Enum): LEFT = 0 RIGHT = 1 - class DamageType(Enum): PHYSICAL = "物理" MAGIC = "法术" @@ -38,7 +37,6 @@ class DamageType(Enum): def __str__(self): return self.value # 直接返回值字符串 - class BuffType(Enum): CHILL = 0 FROZEN = 1 @@ -47,17 +45,15 @@ class BuffType(Enum): CORRUPT = 4 SPEEDUP = 5 DIZZY = 6 - POWER_STONE = 7 # 源石地板 - WINE = 8 # 酒桶的效果 - INVINCIBLE2 = 9 # 转阶段无敌,不会被设为目标 - + POWER_STONE = 7 # 源石地板 + WINE = 8 # 酒桶的效果 + INVINCIBLE2 = 9 # 转阶段无敌,不会被设为目标 class ElementType(Enum): NECRO_LEFT = "凋亡左" # 凋亡元素(原凋亡损伤) NECRO_RIGHT = "凋亡右" # 凋亡元素(原凋亡损伤) FIRE = "灼燃" - def lerp(a, b, x): return a + (b - a) * x @@ -71,10 +67,9 @@ class BuffEffect: data: dict = field(default_factory=dict) -VIRTUAL_TIME_STEP = 30 # 30帧相当于一秒 +VIRTUAL_TIME_STEP = 30 # 30帧相当于一秒 VIRTUAL_TIME_DELTA = 1.0 / VIRTUAL_TIME_STEP - def calculate_normal_dmg(defense, magic_resist, dmg, damageType: DamageType): """计算伤害值""" if damageType == DamageType.PHYSICAL: @@ -83,30 +78,30 @@ def calculate_normal_dmg(defense, magic_resist, dmg, damageType: DamageType): return np.maximum(dmg * 0.05, dmg * (1.0 - magic_resist / 100)) elif damageType == DamageType.TRUE: return dmg - + class SpatialHash: - def __init__(self, battle_field: "Battlefield", cell_size=0.5): + def __init__(self, battle_field : 'Battlefield', cell_size=0.5): self.cell_size = cell_size self.grid = defaultdict(set) # 使用集合避免重复 self.battle_field = battle_field # 记录战场,用来索引敌人信息 self.position_map = {} # 记录每个对象的老位置键 - def _pos_to_key(self, position: FastVector) -> tuple: + def _pos_to_key(self, position : FastVector) -> tuple: """将坐标转换为网格键""" return ( int(math.floor(position.x / self.cell_size)), - int(math.floor(position.y / self.cell_size)), + int(math.floor(position.y / self.cell_size)) ) - def insert(self, position: FastVector, id): + def insert(self, position : FastVector, id): """插入或更新对象位置""" new_key = self._pos_to_key(position) # 如果位置未变化,直接返回 if id in self.position_map and self.position_map[id] == new_key: return - + # 移除旧位置的记录 if id in self.position_map: old_key = self.position_map[id] @@ -122,18 +117,18 @@ def query_neighbors(self, position: FastVector, radius: float) -> set: """查询指定半径内的邻居""" center_x, center_y = (position.x, position.y) neighbors = set() - + # 生成需要检测的网格范围 min_i = int((center_x - radius) / self.cell_size) max_i = int((center_x + radius) / self.cell_size) min_j = int((center_y - radius) / self.cell_size) max_j = int((center_y + radius) / self.cell_size) - + # 遍历所有可能包含邻居的网格 for i in range(min_i, max_i + 1): for j in range(min_j, max_j + 1): neighbors.update(self.grid.get((i, j), set())) - + return neighbors def batch_update(self, updates: dict): @@ -141,33 +136,27 @@ def batch_update(self, updates: dict): for obj_id, pos in updates.items(): self.insert(obj_id, pos) - -def load_monster_mapping_from_csv(file_path="src/resources/data/monster.csv"): +def load_monster_mapping_from_csv(file_path='monster.csv'): """从CSV文件加载怪物ID和原始名称的映射""" mapping = {} try: - with open(file_path, mode="r", encoding="utf-8-sig") as csvfile: + with open(file_path, mode='r', encoding='utf-8-sig') as csvfile: reader = csv.DictReader(csvfile) for row in reader: try: - monster_id = int(row["id"]) - 1 # 转为0-based索引 - original_name = row["原始名称"] + monster_id = int(row['id']) - 1 # 转为0-based索引 + original_name = row['原始名称'] mapping[monster_id] = original_name except ValueError: - print( - f"Skipping row due to invalid ID or missing '原始名称': {row}" - ) + print(f"Skipping row due to invalid ID or missing '原始名称': {row}") except FileNotFoundError: print(f"Error: {file_path} not found. Using empty monster mapping.") except Exception as e: logger.exception(f"Error loading monster mapping from CSV: {e}") return mapping - # ID与怪物名称映射表 -MONSTER_MAPPING = load_monster_mapping_from_csv( - "src/resources/data/monster_greenvine.csv" -) +MONSTER_MAPPING = load_monster_mapping_from_csv('monster_greenvine.csv') # 创建反向映射字典(名字到ID) -REVERSE_MONSTER_MAPPING = {name: id for id, name in MONSTER_MAPPING.items()} +REVERSE_MONSTER_MAPPING = {name: id for id, name in MONSTER_MAPPING.items()} \ No newline at end of file diff --git a/src/simulation/vector2d.py b/simulator/vector2d.py similarity index 85% rename from src/simulation/vector2d.py rename to simulator/vector2d.py index 4841d93..a55ee2b 100644 --- a/src/simulation/vector2d.py +++ b/simulator/vector2d.py @@ -1,6 +1,5 @@ import math from typing import Tuple - # import numpy as np # from numba.experimental import jitclass # from numba import float64 @@ -20,103 +19,101 @@ # # 基础运算 # def __add__(self, other: 'Vector2D') -> 'Vector2D': # return Vector2D(self.x + other.x, self.y + other.y) - + # def __sub__(self, other: 'Vector2D') -> 'Vector2D': # return Vector2D(self.x - other.x, self.y - other.y) - + # def __mul__(self, scalar: float) -> 'Vector2D': # return Vector2D(self.x * scalar, self.y * scalar) - + # # 高性能运算方法 # @property # def magnitude(self) -> float: # """模长计算优化(SIMD加速)""" # return math.hypot(self.x, self.y) # 使用hypot避免溢出 - + # def normalized(self) -> 'Vector2D': # """单位向量(零除保护)""" # mag = self.magnitude # return Vector2D(self.x/mag, self.y/mag) if mag != 0 else Vector2D(0, 0) - + # def dot(self, other: 'Vector2D') -> float: # """点积(寄存器优化)""" # return self.x*other.x + self.y*other.y - + # def rotate(self, radians: float) -> 'Vector2D': # """旋转优化(预计算sin/cos)""" # c = math.cos(radians) # s = math.sin(radians) # return Vector2D(self.x*c - self.y*s, self.x*s + self.y*c) - # 纯Python优化方案(兼容性更好) class FastVector: - __slots__ = ("x", "y") # 减少内存占用约40% - + __slots__ = ('x', 'y') # 减少内存占用约40% + def __init__(self, x: float, y: float): self.x = x self.y = y - - def __sub__(self, other: "FastVector") -> "FastVector": + + def __sub__(self, other: 'FastVector') -> 'FastVector': return self.__class__(self.x - other.x, self.y - other.y) - def __add__(self, other: "FastVector") -> "FastVector": + def __add__(self, other: 'FastVector') -> 'FastVector': return self.__class__(self.x + other.x, self.y + other.y) - - def __truediv__(self, other: float) -> "FastVector": + + def __truediv__(self, other: float) -> 'FastVector': return self.__class__(self.x / other, self.y / other) - - def __mul__(self, other: float) -> "FastVector": + + def __mul__(self, other: float) -> 'FastVector': return self.__class__(self.x * other, self.y * other) - - def __iadd__(self, other: "FastVector") -> "FastVector": + + def __iadd__(self, other: 'FastVector') -> 'FastVector': """就地加法优化(减少对象创建)""" self.x += other.x self.y += other.y return self - + @property def magnitude_sq(self) -> float: """平方模长(避免开平方)""" return self.x**2 + self.y**2 - + @property def magnitude(self) -> float: """模长""" return math.sqrt(self.x**2 + self.y**2) - - def distance_to(self, other: "FastVector") -> float: + + def distance_to(self, other: 'FastVector') -> float: """快速距离计算""" dx = self.x - other.x dy = self.y - other.y return math.hypot(dx, dy) - + def as_tuple(self) -> Tuple[float, float]: """缓存友好表示""" return (self.x, self.y) - def normalize(self) -> "FastVector": + def normalize(self) -> 'FastVector': d = self.magnitude if d != 0: self.x /= d self.y /= d return self - # # 性能对比测试 # if __name__ == '__main__': # from timeit import timeit - + # # 测试用例 # v1 = Vector2D(3.0, 4.0) # v2 = Vector2D(2.0, 1.0) - + # # Numba版本性能 # print("Numba Add:", timeit(lambda: v1 + v2, number=1_000_000)) # print("Numba Mag:", timeit(lambda: v1.magnitude, number=1_000_000)) - + # # 纯Python版本性能 # fv1 = FastVector(3.0, 4.0) # fv2 = FastVector(2.0, 1.0) # print("Python Add:", timeit(lambda: fv1 + fv2, number=1_000_000)) -# print("Python Mag:", timeit(lambda: fv1.magnitude_sq, number=1_000_000)) +# print("Python Mag:", timeit(lambda: fv1.magnitude_sq, number=1_000_000)) \ No newline at end of file diff --git a/src/simulation/zone.py b/simulator/zone.py similarity index 70% rename from src/simulation/zone.py rename to simulator/zone.py index 4522636..4bc5569 100644 --- a/src/simulation/zone.py +++ b/simulator/zone.py @@ -1,21 +1,23 @@ +from typing import List, Dict, Set from dataclasses import dataclass +import math +import time + +import numpy as np from .vector2d import FastVector from .utils import VIRTUAL_TIME_DELTA, BuffEffect, BuffType - class ZoneType: - POISON = 0 # 毒圈 - WINE = 1 # 酒桶区域 - + POISON = 0 #毒圈 + WINE = 1 #酒桶区域 @dataclass class ZoneEffect: type: str duration: float - class EffectZone: def __init__(self, zone_type: str, position, battle_field): self.zone_type = zone_type @@ -43,46 +45,37 @@ def apply_effect(self, target): raise NotImplementedError def remove_effect(self, target): - """移除区域效果""" + """移除区域效果""" raise NotImplementedError - class PoisonZone(EffectZone): def __init__(self, battle_field): super().__init__(ZoneType.POISON, FastVector(0, 0), battle_field) def apply_effect(self, target): # 添加或更新持续伤害效果 - target.status_system.apply( - BuffEffect( - type=BuffType.POWER_STONE, - duration=VIRTUAL_TIME_DELTA * 2, - source=self, - ) - ) + target.status_system.apply(BuffEffect( + type=BuffType.POWER_STONE, + duration=VIRTUAL_TIME_DELTA * 2, + source=self + )) def contains(self, target) -> bool: """判断点是否在区域内""" if self.battle_field.danger_zone_size() > 0: size = self.battle_field.danger_zone_size() - if ( - target.position.x < size + 1 - or target.position.x > self.battle_field.map_size[0] - size - 1 - ) or ( - target.position.y < size - or target.position.y > self.battle_field.map_size[1] - size - ): + if (target.position.x < size + 1 or target.position.x > self.battle_field.map_size[0] - size - 1)\ + or (target.position.y < size or target.position.y > self.battle_field.map_size[1] - size): return True return False - - + class WineZone(EffectZone): def __init__(self, position, battle_field, duration, faction): super().__init__(ZoneType.WINE, position, battle_field) self.duration = duration self.radius = 2 self.faction = faction - + def should_clear(self, delta_time) -> bool: """场地效果清除逻辑""" return self.duration <= 0 @@ -93,16 +86,12 @@ def update(self, delta_time): def apply_effect(self, target): # 添加或更新持续伤害效果 - target.status_system.apply( - BuffEffect( - type=BuffType.WINE, - duration=VIRTUAL_TIME_DELTA * 2, - source=self, - ) - ) + target.status_system.apply(BuffEffect( + type=BuffType.WINE, + duration=VIRTUAL_TIME_DELTA * 2, + source=self + )) def contains(self, target) -> bool: """判断点是否在区域内""" - return ( - target.position - self.position - ).magnitude <= self.radius and target.faction == self.faction + return (target.position - self.position).magnitude <= self.radius and target.faction == self.faction \ No newline at end of file diff --git a/specialmonster.py b/specialmonster.py new file mode 100644 index 0000000..708cd64 --- /dev/null +++ b/specialmonster.py @@ -0,0 +1,37 @@ +class SpecialMonsterHandler: + """ + 可按照格式自行添加特殊怪物的留言信息: + 格式: + 怪物ID: { + 'name': '怪物名称', # 可留空 + 'win_message': '胜利时显示的消息', # 可留空 + 'lose_message': '失败时显示的消息', # 可留空 + } + """ + def __init__(self): + self.special_monsters = { + 1: { + 'name': '狗神', + 'win_message': "全军出击,我咬死你!", + 'lose_message': "牙崩了牙崩了" + }, + } + + def check_special_monsters(self, left_monsters, right_monsters, winner): + messages = [] + + for monster_id, config in self.special_monsters.items(): + left_has = left_monsters[str(monster_id)].text().isdigit() and int(left_monsters[str(monster_id)].text()) > 0 + right_has = right_monsters[str(monster_id)].text().isdigit() and int(right_monsters[str(monster_id)].text()) > 0 + + if left_has or right_has: + if winner == "左方" and left_has and config['win_message']: + messages.append(config['win_message']) + elif winner == "右方" and right_has and config['win_message']: + messages.append(config['win_message']) + elif winner == "右方" and left_has and config['lose_message']: + messages.append(config['lose_message']) + elif winner == "左方" and right_has and config['lose_message']: + messages.append(config['lose_message']) + + return "\n".join(messages) \ No newline at end of file diff --git a/src/analysis/winning_rate_statistics.py b/src/analysis/winning_rate_statistics.py deleted file mode 100644 index 1daa56c..0000000 --- a/src/analysis/winning_rate_statistics.py +++ /dev/null @@ -1,1131 +0,0 @@ -import pandas as pd -from math import sqrt -from collections import defaultdict -from src.core.config import MONSTER_COUNT, FIELD_FEATURE_COUNT, MONSTER_DATA -from src.core.paths import image_path, PROJECT_ROOT - -FIELD_FEATURE_COUNT = 0 - - -def load_data(): - """加载数据""" - df = pd.read_csv( - PROJECT_ROOT / "data" / "arknights.csv", header=None, low_memory=False - ) - # 设置列名 - monster_cols_left = [f"L{i+1}" for i in range(MONSTER_COUNT)] - field_cols_left = [f"FL{i+1}" for i in range(FIELD_FEATURE_COUNT)] - monster_cols_right = [f"R{i+1}" for i in range(MONSTER_COUNT)] - field_cols_right = [f"FR{i+1}" for i in range(FIELD_FEATURE_COUNT)] - - df.columns = ( - monster_cols_left - + field_cols_left - + monster_cols_right - + field_cols_right - + ["Result", "ImgPath"] - ) - return df - - -def get_monster_name(monster_id): - """根据怪物ID获取怪物名称""" - if monster_id in MONSTER_DATA.index: - return MONSTER_DATA.loc[monster_id]["名称"] - return f"怪物{monster_id}" - - -def get_monster_original_name(monster_id): - """根据怪物ID获取怪物原始名称(用于匹配图片)""" - if monster_id in MONSTER_DATA.index: - return MONSTER_DATA.loc[monster_id]["原始名称"] - return f"怪物{monster_id}" - - -def monster_image_uri(monster_name): - path = image_path(monster_name) - if not path.exists(): - path = image_path("empty") - return path.resolve().as_uri() - - -def monster_img_tag(monster_name, size): - uri = monster_image_uri(monster_name) - empty_uri = monster_image_uri("empty") - return ( - f'' - ) - - -def calculate_all_monster_win_rates(df): - """计算所有怪物的胜率""" - monster_stats = {} - total_matches = len(df) - - for i in range(1, MONSTER_COUNT + 1): - try: - # 转换数据类型并过滤 - df[f"L{i}"] = pd.to_numeric(df[f"L{i}"], errors="coerce").fillna(0) - df[f"R{i}"] = pd.to_numeric(df[f"R{i}"], errors="coerce").fillna(0) - - # 左方统计 - left_games = df[df[f"L{i}"] != 0] - left_wins = len(left_games[left_games["Result"] == "L"]) - left_total = len(left_games) - - # 右方统计 - right_games = df[df[f"R{i}"] != 0] - right_wins = len(right_games[right_games["Result"] == "R"]) - right_total = len(right_games) - except Exception as e: - print(f"处理怪物{i}时出错: {e}") - continue - - # 合并统计 - total_games = left_total + right_total - total_wins = left_wins + right_wins - - if total_games > 0: - monster_name = get_monster_name(i) - win_rate = total_wins / total_games - participation_rate = ( - total_games / total_matches if total_matches > 0 else 0 - ) - monster_stats[monster_name] = { - "怪物ID": i, - "胜场": total_wins, - "总场数": total_games, - "胜率": win_rate, - "参战率": participation_rate, - } - - return pd.DataFrame(monster_stats).T.sort_values("胜率", ascending=False) - - -def analyze_monster_combinations(df): - """分析怪物配合效果""" - # 初始化数据结构 - single_stats = defaultdict(lambda: {"appearances": 0, "wins": 0}) - pair_stats = defaultdict(lambda: {"co_occurrences": 0, "co_wins": 0}) - - for _, record in df.iterrows(): - victory_side = record["Result"] - - # 获取左右两方的怪物 - left_monsters = [] - right_monsters = [] - - for i in range(1, MONSTER_COUNT + 1): - try: - left_val = ( - float(record[f"L{i}"]) if pd.notna(record[f"L{i}"]) else 0 - ) - right_val = ( - float(record[f"R{i}"]) if pd.notna(record[f"R{i}"]) else 0 - ) - - if left_val > 0: - left_monsters.append(i) - if right_val > 0: - right_monsters.append(i) - except (ValueError, TypeError): - continue - - # 确定胜利队伍和失败队伍 - if victory_side == "L": - win_team, lose_team = left_monsters, right_monsters - else: - win_team, lose_team = right_monsters, left_monsters - - # 更新单怪统计(胜利方) - for monster in win_team: - single_stats[monster]["appearances"] += 1 - single_stats[monster]["wins"] += 1 - - # 更新单怪统计(失败方) - for monster in lose_team: - single_stats[monster]["appearances"] += 1 - - # 更新双怪组合统计(胜利方) - for i in range(len(win_team)): - for j in range(i + 1, len(win_team)): - x, y = sorted((win_team[i], win_team[j])) - pair_stats[(x, y)]["co_occurrences"] += 1 - pair_stats[(x, y)]["co_wins"] += 1 - - # 更新双怪组合统计(失败方) - for i in range(len(lose_team)): - for j in range(i + 1, len(lose_team)): - x, y = sorted((lose_team[i], lose_team[j])) - pair_stats[(x, y)]["co_occurrences"] += 1 - - # 计算最佳配合 - results = [] - total_battles = len(df) - - for (x, y), stats in pair_stats.items(): - if stats["co_occurrences"] < 10: # 过滤低频组合 - continue - - # 组合胜率 - xy_win_rate = stats["co_wins"] / stats["co_occurrences"] - - # 单怪胜率 - if ( - single_stats[x]["appearances"] > 0 - and single_stats[y]["appearances"] > 0 - ): - x_win_rate = ( - single_stats[x]["wins"] / single_stats[x]["appearances"] - ) - y_win_rate = ( - single_stats[y]["wins"] / single_stats[y]["appearances"] - ) - - # 提升度 - 简化计算,不使用卡方检验 - expected_win_rate = sqrt(x_win_rate * y_win_rate) - lift = ( - xy_win_rate / expected_win_rate if expected_win_rate > 0 else 0 - ) - - if ( - lift > 1.1 - and xy_win_rate > max(x_win_rate, y_win_rate) - and stats["co_occurrences"] >= 20 - ): - x_name = get_monster_name(x) - y_name = get_monster_name(y) - results.append( - { - "组合": f"{x_name}+{y_name}", - "怪物1": x_name, - "怪物2": y_name, - "ID1": x, - "ID2": y, - "提升度": lift, - "组合胜率": xy_win_rate, - "出场次数": stats["co_occurrences"], - "获胜次数": stats["co_wins"], - } - ) - - # 按提升度排序 - results.sort(key=lambda x: -x["提升度"]) - return pd.DataFrame(results) - - -def find_countered_monsters(df): - """寻找被克制的怪物前五个""" - counter_stats = defaultdict(lambda: {"total_matchups": 0, "losses": 0}) - - for _, record in df.iterrows(): - victory_side = record["Result"] - - # 获取左右两方的怪物 - left_monsters = [] - right_monsters = [] - - for i in range(1, MONSTER_COUNT + 1): - try: - left_val = ( - float(record[f"L{i}"]) if pd.notna(record[f"L{i}"]) else 0 - ) - right_val = ( - float(record[f"R{i}"]) if pd.notna(record[f"R{i}"]) else 0 - ) - - if left_val > 0: - left_monsters.append(i) - if right_val > 0: - right_monsters.append(i) - except (ValueError, TypeError): - continue - - # 分析对战情况 - for left_monster in left_monsters: - for right_monster in right_monsters: - # 左方怪物的统计 - counter_stats[left_monster]["total_matchups"] += 1 - if victory_side == "R": # 左方败北 - counter_stats[left_monster]["losses"] += 1 - - # 右方怪物的统计 - counter_stats[right_monster]["total_matchups"] += 1 - if victory_side == "L": # 右方败北 - counter_stats[right_monster]["losses"] += 1 - - # 计算被克制率 - countered_results = [] - for monster_id, stats in counter_stats.items(): - if stats["total_matchups"] >= 20: # 至少20场对战 - loss_rate = stats["losses"] / stats["total_matchups"] - monster_name = get_monster_name(monster_id) - countered_results.append( - { - "怪物": monster_name, - "怪物ID": monster_id, - "被克制率": loss_rate, - "败场": stats["losses"], - "总对战数": stats["total_matchups"], - } - ) - - # 按被克制率排序(降序) - countered_results.sort(key=lambda x: -x["被克制率"]) - return pd.DataFrame(countered_results[:5]) - - -def analyze_individual_monster_relations(df): - """分析每个怪物的详细关系:最佳队友、克制关系、被克制关系""" - monster_relations = {} - - # 初始化数据结构 - single_stats = defaultdict(lambda: {"appearances": 0, "wins": 0}) - pair_stats = defaultdict(lambda: {"co_occurrences": 0, "co_wins": 0}) - counter_stats = defaultdict( - lambda: defaultdict(lambda: {"matchups": 0, "wins": 0}) - ) - - for _, record in df.iterrows(): - victory_side = record["Result"] - - # 获取左右两方的怪物 - left_monsters = [] - right_monsters = [] - - for i in range(1, MONSTER_COUNT + 1): - try: - left_val = ( - float(record[f"L{i}"]) if pd.notna(record[f"L{i}"]) else 0 - ) - right_val = ( - float(record[f"R{i}"]) if pd.notna(record[f"R{i}"]) else 0 - ) - - if left_val > 0: - left_monsters.append(i) - if right_val > 0: - right_monsters.append(i) - except (ValueError, TypeError): - continue - - # 确定胜利队伍和失败队伍 - if victory_side == "L": - win_team, lose_team = left_monsters, right_monsters - else: - win_team, lose_team = right_monsters, left_monsters - - # 更新单怪统计 - for monster in win_team: - single_stats[monster]["appearances"] += 1 - single_stats[monster]["wins"] += 1 - - for monster in lose_team: - single_stats[monster]["appearances"] += 1 - - # 更新队友统计 - for i in range(len(win_team)): - for j in range(i + 1, len(win_team)): - x, y = sorted((win_team[i], win_team[j])) - pair_stats[(x, y)]["co_occurrences"] += 1 - pair_stats[(x, y)]["co_wins"] += 1 - - for i in range(len(lose_team)): - for j in range(i + 1, len(lose_team)): - x, y = sorted((lose_team[i], lose_team[j])) - pair_stats[(x, y)]["co_occurrences"] += 1 - - # 更新克制关系统计 - for winner in win_team: - for loser in lose_team: - counter_stats[winner][loser]["matchups"] += 1 - counter_stats[winner][loser]["wins"] += 1 - counter_stats[loser][winner]["matchups"] += 1 - - # 为每个怪物分析关系 - for monster_id in range(1, MONSTER_COUNT + 1): - monster_name = get_monster_name(monster_id) - - if single_stats[monster_id]["appearances"] < 10: # 数据量太少 - continue - - # 分析最佳队友 - best_teammates = [] - for (x, y), stats in pair_stats.items(): - if x == monster_id or y == monster_id: - partner_id = y if x == monster_id else x - if stats["co_occurrences"] >= 5: # 至少5次合作 - combo_win_rate = stats["co_wins"] / stats["co_occurrences"] - - # 计算提升度 - if ( - single_stats[monster_id]["appearances"] > 0 - and single_stats[partner_id]["appearances"] > 0 - ): - monster_win_rate = ( - single_stats[monster_id]["wins"] - / single_stats[monster_id]["appearances"] - ) - partner_win_rate = ( - single_stats[partner_id]["wins"] - / single_stats[partner_id]["appearances"] - ) - expected_win_rate = sqrt( - monster_win_rate * partner_win_rate - ) - - if expected_win_rate > 0: - lift = combo_win_rate / expected_win_rate - if lift > 1.0: - best_teammates.append( - { - "partner_id": partner_id, - "partner_name": get_monster_name( - partner_id - ), - "lift": lift, - "combo_win_rate": combo_win_rate, - "occurrences": stats["co_occurrences"], - } - ) - - best_teammates.sort(key=lambda x: -x["lift"]) - - # 分析克制关系 - counters = [] # 该怪物克制的 - countered_by = [] # 克制该怪物的 - - for opponent_id in counter_stats[monster_id]: - stats = counter_stats[monster_id][opponent_id] - if stats["matchups"] >= 5: - win_rate = stats["wins"] / stats["matchups"] - if win_rate > 0.6: # 胜率超过60%认为克制 - counters.append( - { - "opponent_id": opponent_id, - "opponent_name": get_monster_name(opponent_id), - "win_rate": win_rate, - "matchups": stats["matchups"], - } - ) - - for opponent_id in range(1, MONSTER_COUNT + 1): - if ( - opponent_id in counter_stats - and monster_id in counter_stats[opponent_id] - ): - stats = counter_stats[opponent_id][monster_id] - if stats["matchups"] >= 5: - lose_rate = stats["wins"] / stats["matchups"] - if lose_rate > 0.6: # 对方胜率超过60%认为被克制 - countered_by.append( - { - "opponent_id": opponent_id, - "opponent_name": get_monster_name(opponent_id), - "lose_rate": lose_rate, - "matchups": stats["matchups"], - } - ) - - counters.sort(key=lambda x: -x["win_rate"]) - countered_by.sort(key=lambda x: -x["lose_rate"]) - - monster_relations[monster_id] = { - "name": monster_name, - "best_teammates": best_teammates[:3], - "counters": counters[:3], - "countered_by": countered_by[:3], - } - - return monster_relations - - -def get_terrain_feature_columns(): - """获取地形特征列名""" - import json - import re - from collections import defaultdict - - try: - # 加载类别映射 - class_map_path = "tools/battlefield_recognize/class_to_idx.json" - with open(class_map_path, "r", encoding="utf-8") as f: - class_to_idx = json.load(f) - - # 使用与data_cleaning_with_field_recognize_gpu.py相同的逻辑 - grouped_elements = defaultdict(list) - for class_name in class_to_idx.keys(): - if class_name.endswith("_none"): - continue - condensed_name = re.sub(r"_left_", "_", class_name) - condensed_name = re.sub(r"_right_", "_", condensed_name) - grouped_elements[condensed_name].append(class_name) - - # 返回排序后的特征列名 - return sorted(grouped_elements.keys()) - except Exception as e: - print(f"无法获取地形特征列名,使用默认值: {e}") - # 如果无法获取,返回默认列表 - return [ - "altar_vertical_altar", - "block_parallel_block", - "block_vertical_altar_shape1", - "block_vertical_altar_shape2", - "block_vertical_block_shape1", - "block_vertical_block_shape2", - "coil_narrow_coil", - "coil_wide_coil", - "crossbow_top_crossbow", - "fire_side_crossbow", - "fire_side_fire", - "fire_top_fire", - ] - - -def analyze_terrain_effects(df): - """分析地形对怪物的影响""" - terrain_effects = [] - - # 获取实际的地形特征列名 - terrain_feature_columns = get_terrain_feature_columns() - - # 地形显示名称映射 - terrain_display_mapping = { - "altar_vertical_altar": "垂直祭坛", - "block_parallel_block": "平行方块阻挡", - "block_vertical_altar_shape1": "垂直祭坛形阻挡1", - "block_vertical_altar_shape2": "垂直祭坛形阻挡2", - "block_vertical_block_shape1": "垂直方块阻挡1", - "block_vertical_block_shape2": "垂直方块阻挡2", - "coil_narrow_coil": "窄型线圈装置", - "coil_wide_coil": "宽型线圈装置", - "crossbow_top_crossbow": "顶部弩炮", - "fire_side_crossbow": "侧边弩炮", - "fire_side_fire": "侧边火炮", - "fire_top_fire": "顶部火炮", - } - - for terrain_idx, terrain_key in enumerate(terrain_feature_columns): - terrain_name = terrain_display_mapping.get(terrain_key, terrain_key) - - for monster_idx in range(1, MONSTER_COUNT + 1): - monster_name = get_monster_name(monster_idx) - - # 转换数据类型 - df[f"FL{terrain_idx+1}"] = pd.to_numeric( - df[f"FL{terrain_idx+1}"], errors="coerce" - ).fillna(0) - df[f"FR{terrain_idx+1}"] = pd.to_numeric( - df[f"FR{terrain_idx+1}"], errors="coerce" - ).fillna(0) - df[f"L{monster_idx}"] = pd.to_numeric( - df[f"L{monster_idx}"], errors="coerce" - ).fillna(0) - df[f"R{monster_idx}"] = pd.to_numeric( - df[f"R{monster_idx}"], errors="coerce" - ).fillna(0) - - # 有地形时的表现 - terrain_left_games = df[ - (df[f"FL{terrain_idx+1}"] == 1) & (df[f"L{monster_idx}"] > 0) - ] - terrain_right_games = df[ - (df[f"FR{terrain_idx+1}"] == 1) & (df[f"R{monster_idx}"] > 0) - ] - - terrain_total = len(terrain_left_games) + len(terrain_right_games) - if terrain_total < 5: # 数据量太少 - continue - - terrain_wins = len( - terrain_left_games[terrain_left_games["Result"] == "L"] - ) + len(terrain_right_games[terrain_right_games["Result"] == "R"]) - terrain_win_rate = terrain_wins / terrain_total - - # 无地形时的表现 - normal_left_games = df[ - (df[f"FL{terrain_idx+1}"] == 0) & (df[f"L{monster_idx}"] > 0) - ] - normal_right_games = df[ - (df[f"FR{terrain_idx+1}"] == 0) & (df[f"R{monster_idx}"] > 0) - ] - - normal_total = len(normal_left_games) + len(normal_right_games) - if normal_total < 5: - continue - - normal_wins = len( - normal_left_games[normal_left_games["Result"] == "L"] - ) + len(normal_right_games[normal_right_games["Result"] == "R"]) - normal_win_rate = normal_wins / normal_total - - # 计算影响程度 - effect = terrain_win_rate - normal_win_rate - - if abs(effect) >= 0.05: # 胜率差异超过5%才记录 - terrain_effects.append( - { - "地形": terrain_name, - "怪物": monster_name, - "怪物ID": monster_idx, - "地形胜率": terrain_win_rate, - "普通胜率": normal_win_rate, - "影响程度": effect, - "地形场次": terrain_total, - "普通场次": normal_total, - } - ) - - # 按影响程度绝对值排序 - terrain_effects.sort(key=lambda x: -abs(x["影响程度"])) - return pd.DataFrame(terrain_effects[:20]) # 增加到前20个 - - -def analyze_device_counter_effects(df): - """分析五个装置对怪物的克制效果""" - device_counter_results = {} - - # 获取实际的地形特征列名 - terrain_feature_columns = get_terrain_feature_columns() - - # 定义五个装置类别及其对应的地形特征 - device_categories = { - "altar": { - "name": "祭坛", - "features": [f for f in terrain_feature_columns if "altar" in f], - "description": "祭坛类装置", - }, - "block": { - "name": "箱子/阻挡", - "features": [f for f in terrain_feature_columns if "block" in f], - "description": "方块阻挡类装置", - }, - "coil": { - "name": "电桩", - "features": [f for f in terrain_feature_columns if "coil" in f], - "description": "线圈电桩装置", - }, - "crossbow": { - "name": "弩箭", - "features": [ - f for f in terrain_feature_columns if "crossbow" in f - ], - "description": "弩炮装置", - }, - "fire": { - "name": "火炮", - "features": [f for f in terrain_feature_columns if "fire" in f], - "description": "火炮装置", - }, - } - - for device_key, device_info in device_categories.items(): - device_name = device_info["name"] - device_features = device_info["features"] - - if not device_features: - continue - - device_effects = [] - - # 对每个怪物分析该装置的克制效果 - for monster_idx in range(1, MONSTER_COUNT + 1): - monster_name = get_monster_name(monster_idx) - - # 收集该装置所有特征的统计数据 - total_device_games = 0 - total_device_wins = 0 - total_normal_games = 0 - total_normal_wins = 0 - - for terrain_key in device_features: - if terrain_key not in terrain_feature_columns: - continue - - terrain_idx = terrain_feature_columns.index(terrain_key) - - # 转换数据类型 - df[f"FL{terrain_idx+1}"] = pd.to_numeric( - df[f"FL{terrain_idx+1}"], errors="coerce" - ).fillna(0) - df[f"FR{terrain_idx+1}"] = pd.to_numeric( - df[f"FR{terrain_idx+1}"], errors="coerce" - ).fillna(0) - df[f"L{monster_idx}"] = pd.to_numeric( - df[f"L{monster_idx}"], errors="coerce" - ).fillna(0) - df[f"R{monster_idx}"] = pd.to_numeric( - df[f"R{monster_idx}"], errors="coerce" - ).fillna(0) - - # 有该装置时的表现 - device_left_games = df[ - (df[f"FL{terrain_idx+1}"] == 1) - & (df[f"L{monster_idx}"] > 0) - ] - device_right_games = df[ - (df[f"FR{terrain_idx+1}"] == 1) - & (df[f"R{monster_idx}"] > 0) - ] - - device_games_count = len(device_left_games) + len( - device_right_games - ) - device_wins_count = len( - device_left_games[device_left_games["Result"] == "L"] - ) + len( - device_right_games[device_right_games["Result"] == "R"] - ) - - # 无该装置时的表现 - normal_left_games = df[ - (df[f"FL{terrain_idx+1}"] == 0) - & (df[f"L{monster_idx}"] > 0) - ] - normal_right_games = df[ - (df[f"FR{terrain_idx+1}"] == 0) - & (df[f"R{monster_idx}"] > 0) - ] - - normal_games_count = len(normal_left_games) + len( - normal_right_games - ) - normal_wins_count = len( - normal_left_games[normal_left_games["Result"] == "L"] - ) + len( - normal_right_games[normal_right_games["Result"] == "R"] - ) - - total_device_games += device_games_count - total_device_wins += device_wins_count - total_normal_games += normal_games_count - total_normal_wins += normal_wins_count - - # 计算整体效果 - if ( - total_device_games >= 10 and total_normal_games >= 10 - ): # 确保有足够的数据 - device_win_rate = total_device_wins / total_device_games - normal_win_rate = total_normal_wins / total_normal_games - effect = device_win_rate - normal_win_rate - - # 计算克制程度(负值表示被该装置克制) - counter_effect = -effect # 装置对怪物的克制效果 - - if abs(effect) >= 0.05: # 胜率差异超过5%才记录 - device_effects.append( - { - "怪物": monster_name, - "怪物ID": monster_idx, - "装置胜率": device_win_rate, - "普通胜率": normal_win_rate, - "克制程度": counter_effect, # 正值表示被该装置克制 - "装置场次": total_device_games, - "普通场次": total_normal_games, - "效果类型": ( - "被克制" if counter_effect > 0 else "克制装置" - ), - } - ) - - # 按克制程度排序(被克制程度最高的在前) - device_effects.sort(key=lambda x: -x["克制程度"]) - device_counter_results[device_key] = { - "name": device_name, - "description": device_info["description"], - "features": device_features, - "effects": device_effects[:10], # 取前10个被克制最严重的怪物 - } - - return device_counter_results - - -def create_html_table( - df, columns, title, is_combo=False, monster_relations=None -): - """创建带有怪物头像的HTML表格""" - html = f"

{title}

\n\n" - - # 表头 - if is_combo: - html += "" - else: - html += "" - - for col in columns: - html += f"" - - # 如果是胜率表且有关系数据,添加额外的列 - if not is_combo and monster_relations and title == "所有怪物胜率排行榜": - html += "" - - html += "\n" - - # 表格内容 - row_number = 1 - for idx, row in df.iterrows(): - html += "" - - # 怪物图片和名称 - monster_id = None - if is_combo and "ID1" in row and "ID2" in row: - monster1_name = get_monster_name(row["ID1"]) - monster2_name = get_monster_name(row["ID2"]) - monster1_orig = get_monster_original_name(row["ID1"]) - monster2_orig = get_monster_original_name(row["ID2"]) - html += f"""""" - elif "怪物ID" in row: - monster_name = get_monster_name(row["怪物ID"]) - monster_orig = get_monster_original_name(row["怪物ID"]) - display_name = row.get("怪物", monster_name) - monster_id = row["怪物ID"] - html += f"""""" - else: - # 对于胜率表,使用索引作为怪物名称 - monster_name = idx - # 尝试从怪物数据中获取ID - monster_orig = monster_name - for mid in range(1, MONSTER_COUNT + 1): - if get_monster_name(mid) == monster_name: - monster_id = mid - monster_orig = get_monster_original_name(mid) - break - html += f"""""" - - # 数据列 - for col in columns: - value = row[col] - if isinstance(value, float): - if "率" in col or "程度" in col: - html += f"" - else: - html += f"" - else: - html += f"" - - # 如果是胜率表且有关系数据,添加关系信息 - if ( - not is_combo - and monster_relations - and title == "所有怪物胜率排行榜" - and monster_id - ): - relations = monster_relations.get(int(monster_id), {}) - - # 最佳队友 - html += "" - - # 克制关系 - html += "" - - # 被克制关系 - html += "" - - html += "\n" - row_number += 1 - - html += "
组合怪物{col}最佳队友克制被克制
- {row_number}. - {monster_img_tag(monster1_orig, 30)} - {monster1_name}
- {monster_img_tag(monster2_orig, 30)} - {monster2_name} -
- {row_number}. - {monster_img_tag(monster_orig, 50)} - {display_name} - - {row_number}. - {monster_img_tag(monster_orig, 50)} - {monster_name} - {value:.2%}{value:.2f}{value}" - if ( - relations - and "best_teammates" in relations - and relations["best_teammates"] - ): - teammates = [] - for teammate in relations["best_teammates"]: - teammate_orig = get_monster_original_name( - teammate["partner_id"] - ) - teammates.append(f"""
- {monster_img_tag(teammate_orig, 20)} - {teammate['partner_name']} ({teammate['lift']:.2f}x) -
""") - html += "".join(teammates) - else: - html += "暂无数据" - html += "
" - if relations and "counters" in relations and relations["counters"]: - counters = [] - for counter in relations["counters"]: - counter_orig = get_monster_original_name( - counter["opponent_id"] - ) - counters.append(f"""
- {monster_img_tag(counter_orig, 20)} - {counter['opponent_name']} ({counter['win_rate']:.0%}) -
""") - html += "".join(counters) - else: - html += "暂无数据" - html += "
" - if ( - relations - and "countered_by" in relations - and relations["countered_by"] - ): - countered = [] - for counter in relations["countered_by"]: - counter_orig = get_monster_original_name( - counter["opponent_id"] - ) - countered.append(f"""
- {monster_img_tag(counter_orig, 20)} - {counter['opponent_name']} ({counter['lose_rate']:.0%}) -
""") - html += "".join(countered) - else: - html += "暂无数据" - html += "
\n" - return html - - -def create_device_counter_html(device_counter_effects): - """创建装置克制效果的HTML表格""" - html = "

装置克制效果统计

\n" - - for device_key, device_data in device_counter_effects.items(): - if not device_data["effects"]: - continue - - device_name = device_data["name"] - device_description = device_data["description"] - effects = device_data["effects"] - - html += f"

{device_name}({device_description})

\n" - html += "\n" - html += "" - html += "\n" - - row_number = 1 - for effect in effects: - monster_name = effect["怪物"] - monster_orig = get_monster_original_name(effect["怪物ID"]) - html += f""" - - - - - - - - \n""" - row_number += 1 - - html += "
怪物克制程度装置胜率普通胜率装置场次普通场次效果类型
- {row_number}. - {monster_img_tag(monster_orig, 40)} - {monster_name} - {effect['克制程度']:.2%}{effect['装置胜率']:.2%}{effect['普通胜率']:.2%}{effect['装置场次']}{effect['普通场次']}{effect['效果类型']}
\n
\n" - - return html - - -def generate_comprehensive_report(): - """生成综合统计报告""" - print("正在加载数据...") - df = load_data() - - print("正在计算怪物胜率...") - win_rates = calculate_all_monster_win_rates(df) - - print("正在分析怪物配合...") - combinations = analyze_monster_combinations(df) - - print("正在分析被克制关系...") - countered = find_countered_monsters(df) - - if FIELD_FEATURE_COUNT > 0: - print("正在分析地形效果...") - terrain_effects = analyze_terrain_effects(df) - - print("正在分析装置克制效果...") - device_counter_effects = analyze_device_counter_effects(df) - else: - print("地形特征数量为0,跳过地形分析...") - terrain_effects = pd.DataFrame() - device_counter_effects = {} - - print("正在分析个体怪物关系...") - monster_relations = analyze_individual_monster_relations(df) - - # 创建HTML报告 - total_battles = len(df) - monster_count = MONSTER_COUNT - field_count = FIELD_FEATURE_COUNT - - html = f""" - - - - - - -

明日方舟争锋频道绿藤城

-
-

数据概览:

-
    -
  • 总战斗记录:{total_battles} 场
  • -
  • 统计怪物数量:{monster_count} 种
  • -
  • 地形特征数量:{field_count} 种
  • -
-
-""" - - # 1. 所有怪物胜率 - if not win_rates.empty: - html += create_html_table( - win_rates, - ["胜场", "总场数", "胜率", "参战率"], - "所有怪物胜率排行榜", - monster_relations=monster_relations, - ) - else: - html += "

所有怪物胜率排行榜

暂无数据

" - - # 2. 最佳配合 - if not combinations.empty: - html += create_html_table( - combinations.head(20), - ["提升度", "组合胜率", "出场次数"], - "最佳怪物配合TOP20", - is_combo=True, - ) - else: - html += "

最佳怪物配合

暂无足够的配合数据

" - - # 4. 地形效果 - if not terrain_effects.empty: - html += create_html_table( - terrain_effects, - ["地形", "地形胜率", "普通胜率", "影响程度"], - "地形影响最大的怪物TOP20", - ) - else: - html += "

地形影响

暂无足够的地形数据

" - - # 5. 装置克制效果 - if device_counter_effects: - html += create_device_counter_html(device_counter_effects) - else: - html += "

装置克制效果统计

暂无足够的装置数据

" - - timestamp = pd.Timestamp.now().strftime("%Y-%m-%d %H:%M:%S") - html += f""" -
-

报告生成时间:{timestamp}

-

注:数据基于历史战斗记录,仅供参考

-
- - -""" - - # 保存报告 - with open("comprehensive_monster_report.html", "w", encoding="utf-8") as f: - f.write(html) - - print("统计报告已生成:comprehensive_monster_report.html") - - # 也输出到控制台 - print("\n=== 怪物胜率TOP10 ===") - print(win_rates.head(10).to_string()) - - if not combinations.empty: - print("\n=== 最佳配合TOP10 ===") - print( - combinations.head(10)[ - ["组合", "提升度", "组合胜率", "出场次数"] - ].to_string() - ) - - # 输出装置克制效果 - if device_counter_effects: - print("\n=== 装置克制效果统计 ===") - for device_key, device_data in device_counter_effects.items(): - if device_data["effects"]: - print( - f"\n{device_data['name']}({device_data['description']})最克制的怪物TOP5:" - ) - for i, effect in enumerate(device_data["effects"][:5]): - print( - f" {i+1}. {effect['怪物']} - 克制程度: {effect['克制程度']:.2%} ({effect['效果类型']})" - ) - - if not terrain_effects.empty: - print("\n=== 地形影响TOP10 ===") - print( - terrain_effects.head(10)[ - ["地形", "怪物", "影响程度", "地形胜率", "普通胜率"] - ].to_string() - ) - - -if __name__ == "__main__": - generate_comprehensive_report() diff --git a/src/core/paths.py b/src/core/paths.py deleted file mode 100644 index 4c9dbec..0000000 --- a/src/core/paths.py +++ /dev/null @@ -1,38 +0,0 @@ -from pathlib import Path - - -PROJECT_ROOT = Path(__file__).resolve().parents[2] -SRC_DIR = PROJECT_ROOT / "src" -RESOURCES_DIR = SRC_DIR / "resources" -ASSETS_DIR = RESOURCES_DIR / "assets" -IMAGES_DIR = ASSETS_DIR / "images" -PROCESS_IMAGES_DIR = IMAGES_DIR / "process" -TMP_IMAGES_DIR = IMAGES_DIR / "tmp" -DATA_DIR = RESOURCES_DIR / "data" -SIMULATION_DIR = SRC_DIR / "simulation" -TOOLS_DIR = SRC_DIR / "tools" - - -def resource_path(*parts: str) -> Path: - return RESOURCES_DIR.joinpath(*parts) - - -def image_path(name: str) -> Path: - return IMAGES_DIR / f"{name}.png" - - -def process_image_path(name: str | int) -> Path: - return PROCESS_IMAGES_DIR / f"{name}.png" - - -def data_path(name: str) -> Path: - return DATA_DIR / name - - -def simulation_path(name: str) -> Path: - return SIMULATION_DIR / name - - -def ensure_tmp_images_dir() -> Path: - TMP_IMAGES_DIR.mkdir(parents=True, exist_ok=True) - return TMP_IMAGES_DIR diff --git a/src/data/__init__.py b/src/data/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/game/__init__.py b/src/game/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/models/__init__.py b/src/models/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/recognition/__init__.py b/src/recognition/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/recognition/specialmonster.py b/src/recognition/specialmonster.py deleted file mode 100644 index 31b9eb5..0000000 --- a/src/recognition/specialmonster.py +++ /dev/null @@ -1,44 +0,0 @@ -class SpecialMonsterHandler: - """ - 可按照格式自行添加特殊怪物的留言信息: - 格式: - 怪物ID: { - 'name': '怪物名称', # 可留空 - 'win_message': '胜利时显示的消息', # 可留空 - 'lose_message': '失败时显示的消息', # 可留空 - } - """ - - def __init__(self): - self.special_monsters = { - 1: { - "name": "狗神", - "win_message": "全军出击,我咬死你!", - "lose_message": "牙崩了牙崩了", - }, - } - - def check_special_monsters(self, left_monsters, right_monsters, winner): - messages = [] - - for monster_id, config in self.special_monsters.items(): - left_has = ( - left_monsters[str(monster_id)].text().isdigit() - and int(left_monsters[str(monster_id)].text()) > 0 - ) - right_has = ( - right_monsters[str(monster_id)].text().isdigit() - and int(right_monsters[str(monster_id)].text()) > 0 - ) - - if left_has or right_has: - if winner == "左方" and left_has and config["win_message"]: - messages.append(config["win_message"]) - elif winner == "右方" and right_has and config["win_message"]: - messages.append(config["win_message"]) - elif winner == "右方" and left_has and config["lose_message"]: - messages.append(config["lose_message"]) - elif winner == "左方" and right_has and config["lose_message"]: - messages.append(config["lose_message"]) - - return "\n".join(messages) diff --git a/src/resources/config/maa_option.json b/src/resources/config/maa_option.json deleted file mode 100644 index 5de39ef..0000000 --- a/src/resources/config/maa_option.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "draw_quality": 85, - "logging": true, - "save_draw": false, - "save_on_error": true, - "stdout_level": 2 -} \ No newline at end of file diff --git a/src/simulation/__init__.py b/src/simulation/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/simulation/scene.json b/src/simulation/scene.json deleted file mode 100644 index 2463240..0000000 --- a/src/simulation/scene.json +++ /dev/null @@ -1,206 +0,0 @@ -{ - "left": [ - { - "name": "酸液源石虫·α", - "count": 0 - }, - { - "name": "高能源石虫", - "count": 0 - }, - { - "name": "阿咬", - "count": 0 - }, - { - "name": "狂暴的猎狗pro", - "count": 0 - }, - { - "name": "提亚卡乌好战者", - "count": 0 - }, - { - "name": "污染躯壳", - "count": 0 - }, - { - "name": "大盾哥", - "count": 0 - }, - { - "name": "宿主流浪者", - "count": 0 - }, - { - "name": "光剑", - "count": 4 - }, - { - "name": "泥岩巨像", - "count": 0 - }, - { - "name": "呼啸骑士团学徒", - "count": 0 - }, - { - "name": "萨卡兹大剑手", - "count": 0 - }, - { - "name": "狂暴宿主组长", - "count": 0 - }, - { - "name": "海螺", - "count": 0 - }, - { - "name": "拳手囚犯", - "count": 0 - }, - { - "name": "高塔术师", - "count": 0 - }, - { - "name": "冰原术师", - "count": 0 - }, - { - "name": "矿脉守卫", - "count": 0 - }, - { - "name": "“庞贝”", - "count": 0 - }, - { - "name": "绵羊", - "count": 0 - }, - { - "name": "食腐狗", - "count": 0 - }, - { - "name": "温顺的武装驮兽", - "count": 3 - }, - { - "name": "鼠鼠", - "count": 4 - }, - { - "name": "“投石机”", - "count": 0 - }, - { - "name": "船长", - "count": 0 - } - ], - "right": [ - { - "name": "酸液源石虫·α", - "count": 0 - }, - { - "name": "高能源石虫", - "count": 0 - }, - { - "name": "阿咬", - "count": 0 - }, - { - "name": "狂暴的猎狗pro", - "count": 0 - }, - { - "name": "提亚卡乌好战者", - "count": 0 - }, - { - "name": "污染躯壳", - "count": 0 - }, - { - "name": "大盾哥", - "count": 0 - }, - { - "name": "宿主流浪者", - "count": 0 - }, - { - "name": "光剑", - "count": 0 - }, - { - "name": "泥岩巨像", - "count": 0 - }, - { - "name": "呼啸骑士团学徒", - "count": 0 - }, - { - "name": "萨卡兹大剑手", - "count": 0 - }, - { - "name": "狂暴宿主组长", - "count": 0 - }, - { - "name": "海螺", - "count": 0 - }, - { - "name": "拳手囚犯", - "count": 11 - }, - { - "name": "高塔术师", - "count": 0 - }, - { - "name": "冰原术师", - "count": 0 - }, - { - "name": "矿脉守卫", - "count": 0 - }, - { - "name": "“庞贝”", - "count": 3 - }, - { - "name": "绵羊", - "count": 9 - }, - { - "name": "食腐狗", - "count": 0 - }, - { - "name": "温顺的武装驮兽", - "count": 0 - }, - { - "name": "鼠鼠", - "count": 0 - }, - { - "name": "“投石机”", - "count": 0 - }, - { - "name": "船长", - "count": 0 - } - ] -} \ No newline at end of file diff --git a/src/simulation/stats.py b/src/simulation/stats.py deleted file mode 100644 index 8ab77cf..0000000 --- a/src/simulation/stats.py +++ /dev/null @@ -1,191 +0,0 @@ -import json -from collections import defaultdict -import numpy as np -import pandas as pd -from scipy.stats import fisher_exact -import matplotlib.pyplot as plt - -# 配置中文字体 -plt.rcParams["font.sans-serif"] = ["Microsoft YaHei"] -plt.rcParams["axes.unicode_minus"] = False - - -def calculate_significance(row): - """计算修正后的统计显著性""" - # 关键参数 - error_total = ( - row["under_estimate"] + row["over_estimate"] - ) # 错误样本总次数 - correct_total = row["total"] - error_total # 正确样本总次数 - - # 过滤无效数据 - if error_total == 0 or correct_total == 0: - return np.nan - - # 低估检验(单侧) - contingency_under = [ - [row["under_estimate"], row["over_estimate"]], - [0, correct_total], - ] - _, under_p = fisher_exact(contingency_under, alternative="greater") - - # 高估检验(单侧) - contingency_over = [ - [row["over_estimate"], row["under_estimate"]], - [0, correct_total], - ] - _, over_p = fisher_exact(contingency_over, alternative="greater") - - return under_p + over_p - - -def analyze_monster_balance(data_path): - # 初始化统计容器 - stats = defaultdict( - lambda: { - "total": 0, # 总出现次数 - "error": 0, # 错误预测中出现次数 - "under_estimate": 0, # 低估计数(实际应胜但预测失败) - "over_estimate": 0, # 高估计数(实际应败但预测成功) - } - ) - - # 数据加载与分析 - with open(data_path, encoding="utf-8") as f: - for line in f: - record = json.loads(line.strip()) - actual = record["result"] - pred = record["pred"] - - for side in ["left", "right"]: - for monster, count in record[side].items(): - # 基础统计 - stats[monster]["total"] += 1 - - # 仅统计预测错误的情况 - if pred != actual: - stats[monster]["error"] += 1 - - # 判断估计方向 - if side == actual: - stats[monster]["under_estimate"] += 1 - elif side != actual: - stats[monster]["over_estimate"] += 1 - - # 构建分析数据框 - df = pd.DataFrame.from_dict(stats, orient="index") - df = df[df["total"] > 0] # 过滤无效数据 - - # 计算统计指标 - df["error_rate"] = df["error"] / df["total"] - df["bias_score"] = (df["under_estimate"] - df["over_estimate"]) / df[ - "total" - ] - df["abs_bias"] = abs(df["bias_score"]) - - # # Fisher精确检验 - # def apply_fisher(row): - # contingency = [ - # [row['under_estimate'], row['over_estimate']], - # [row['total'] - row['under_estimate'], - # row['total'] - row['over_estimate']] - # ] - # _, p_value = fisher_exact(contingency) - # return p_value - - df["p_value"] = df.apply(calculate_significance, axis=1) - - # 筛选显著结果 (p<0.04 且总出现次数>1) - valid = df[(df["p_value"] < 0.04) & (df["total"] >= 1)] - - # 生成可视化 - plot_monster_bias(valid) - - return valid.sort_values("abs_bias", ascending=False) - - -def plot_monster_bias(df): - plt.figure(figsize=(12, 8)) - - # 设置颜色映射 - colors = np.where( - df["bias_score"] > 0, "#4C72B0", "#DD8452" # 蓝色表示低估 - ) # 橙色表示高估 - - # 绘制条形图 - bars = plt.barh(df.index, df["bias_score"], color=colors) - - # 添加统计标注 - for bar, (_, row) in zip(bars, df.iterrows()): - plt.text( - bar.get_width() + 0.02, - bar.get_y() + bar.get_height() / 2, - f"p={row['p_value']:.3f}\nN={row['total']}", - va="center", - ) - - # 图表装饰 - plt.axvline(0, color="gray", linestyle="--") - plt.title("怪物数值平衡性分析", fontsize=14) - plt.xlabel("偏差分数(正值表示低估,负值表示高估)") - plt.ylabel("怪物名称") - plt.xlim(-1.1, 1.1) - plt.grid(axis="x", alpha=0.3) - plt.tight_layout() - plt.savefig("monster_balance.png", dpi=300) - plt.close() - - -# 执行分析 -if __name__ == "__main__": - result_df = analyze_monster_balance("../errors.json") - - # 打印格式化结果 - print("=" * 60) - print( - f"{'怪物名称':<10}\t{'偏差方向':<8}\t{'偏差分数':<8}\t{'p值':<10}\t{'样本量'}" - ) - print("-" * 60) - for name, row in result_df.iterrows(): - direction = "低估" if row["bias_score"] > 0 else "高估" - print( - f"{name:<10}\t{direction:<8}\t{row['bias_score']:.2f}{'*' if row['p_value']<0.01 else '':<4}" - f"\t{row['p_value']:.3f}\t{'':<6}\t{row['total']}" - ) - - plot_monster_bias(result_df) - -with open("../errors.json", encoding="utf-8") as f: - data = f.read() - -counter = defaultdict(int) -power_counter = defaultdict(int) -err_counter = defaultdict(int) - -for line in data.strip().split("\n"): - record = json.loads(line) - result = record["result"] - pred = record["pred"] - for side in ["left", "right"]: - for monster, count in record[side].items(): - counter[monster] += 1 - if pred != result: - if side == result: - # 应该获胜却没有获胜,有低估的可能性 - power_counter[monster] += 1 - else: - # 不该获胜的获胜了,有高估的可能性 - power_counter[monster] -= 1 - err_counter[monster] += 1 - -sorted_monsters = sorted( - err_counter.items(), key=lambda x: (-x[1] / counter[x[0]], x[0]) -) - -for monster, count in sorted_monsters: - str = "低估" if power_counter[monster] > 0 else "高估" - total = counter[monster] - err_total = count - print( - f"{monster}: {err_total}/{total} = {err_total/total} 估计值:【{str}】{-power_counter[monster] / total * 100}" - ) diff --git a/src/tools/battlefield_composite/extract_webm_frames.py b/src/tools/battlefield_composite/extract_webm_frames.py deleted file mode 100644 index 3b667d7..0000000 --- a/src/tools/battlefield_composite/extract_webm_frames.py +++ /dev/null @@ -1,93 +0,0 @@ -import logging -from pathlib import Path -import subprocess - -monster_name = { - "弧光锋卫长": "Arc_Frontliner_Leader", - "炮击组长": "Mortar_Gunner_Leader", - "复仇者": "Hateful_Avenger", - "重装防御者": "Heavy_Defender", - "“庞贝”": "Pompeii", - "冰原术师": "Icefield_Caster", - "沸血骑士团精锐": "Bloodboil_Knightclub_Elite", - "高塔术师": "Spire_Caster", - "固海凿石者": "Ocean_Stonecutter", - # "呼啸骑士团学徒" : "Roar_Knightclub_Trainee", - "湖畔志愿者": "Lakeside_Volunteer", - "杰斯顿·威廉姆斯": "Jesselton_Williams", - "酸液源石虫·α": "Acid_Originium_Slug_α", - "神射手囚犯": "Elite_Sniper_Prisoner", - "拳手囚犯": "Pugilist_Prisoner", - "染污躯壳": "Tainted_Carcass", - "泥岩巨像": "Mudrock_Colossus", - "狂暴的猎狗pro": "Rabid_Hound_Pro", - "宿主拾荒者": "Possessed_Veteran_Junkman", - "狂暴宿主组长": "Enraged_Possessed_Leader", - "萨卡兹大剑手": "Sarkaz_Greatswordsman", - "矿脉守卫": "Vein_Guardian", - "山海众窥魅人": "Shanhaizhong_Seer", - "提亚卡乌好战者": "Tiacauh_Fanatic", - "码头水手": "Dockworker", - "变异巨岩蛛": "Mutant_Giant_Rock_Spider", - "萨卡兹子裔链术师": "Sarkaz_Heirbearer_Chain_Caster", - "温顺的武装驮兽": "Armored_Burdenbeast", - "木裂战士": "Shattered_Champion", - "深溟裂礁者": "Nethersea_Reefbreaker", - "山雪鬼": "Tschäggättä", - "高普尼克": "Gopnik", - "冰爆源石虫": "Infused_Glacial_Originium_Slug", - "反装甲步兵": "Anti-Armor_Infantry", - "“钳钳生风”": "Consortium_of_Pincers", - "富营养的穿刺者": "Nourished_Piercer", - "高级武装人员": "Senior_Armed_Militant", - "朗姆酒推荐者": "Rum_Connoisseur", - "烈酒级醒酒助手": "Whiskey-Grade_Waker-Upper", - "萨卡兹王庭军术师": "Sarkaz_Royal_Court_Caster", - # "灰尾香主" : "Greytail_Leader", - "阵地击人手": "Field_Bludgeoner", - "源石畸变体": "Originiutant", - "提亚卡乌破坏王": "Tiacauh_Annihilator", - "逐腐兽": "Rotchaser", - "高能源石虫": "Infused_Originium_Slug", - "风情街“星术师”": "Fashion_Street_Stellar_Caster", - "田鼷力士": "Fieldmus_Bruiser", - "残党萨克斯手": "Remnant_Saxophonist", - "散华骑士团学徒": "Nova_Knightclub_Trainee", - "“阿咬”": "Bitey", - # "“门”" : "", - "“投石机”": "Catapult", - # "“复仇者”" : '"Hateful Avenger"', - # "扎罗,“狼之主”" : "Zaaro", - # "“自在”" : "Free", - # "灼热源石虫" : "Blazing_Originium_Slug", - # "萨卡兹子裔责罚者" : "Sarkaz_Heirbearer_Punisher", - # "" : "", -} - - -def extract_webm(webm_path: Path, output_folder: Path): - extract_webm_cmd = ( - f'ffmpeg -c:v libvpx -i "{webm_path}" "{output_folder}/frame%d.png"' - ) - subprocess.run(extract_webm_cmd, shell=True, check=True) - - -def main(): - webm_folder = "./tools/battlefield_composite/monster_images" - for webm_path in Path(webm_folder).glob("*.webm"): - name_split = webm_path.parts[-1].split("-") - print(name_split) - if (name := monster_name.get(name_split[0])) is not None: - output_directory = webm_path.parent / (name + "-" + name_split[3]) - print(output_directory) - if not output_directory.exists(): - output_directory.mkdir(exist_ok=True) - extract_webm(webm_path, output_directory) - else: - logging.warning(f"{output_directory} already exists") - else: - logging.error(f"Name: {name_split[0]} not found!") - - -if __name__ == "__main__": - main() diff --git a/src/tools/data_washer_new.py b/src/tools/data_washer_new.py deleted file mode 100644 index cf1ef85..0000000 --- a/src/tools/data_washer_new.py +++ /dev/null @@ -1,1383 +0,0 @@ -import logging -from pathlib import Path -import cv2 -import numpy as np -import csv -import os -import sys -from src.recognition import recognize -import tqdm - -MONSTER_NUM = 56 -black_list_rows = [] - - -def merge(nums): - if not nums: - return "" - intervals = [] - start = end = nums[0] - for num in nums[1:]: - if num == end + 1: - end = num - else: - intervals.append((start, end)) - start = end = num - intervals.append((start, end)) # 添加最后一个区间 - - parts = [] - for s, e in intervals: - if s == e: - parts.append(str(s)) - else: - parts.append(f"{s}-{e}") - return ",".join(parts) - - -def is_continuous_sublist(sub, main): - return any( - sub == main[i : i + len(sub)] for i in range(len(main) - len(sub) + 1) - ) - - -def remove_duplicate_subsequences_easy(listdata, threshold=3): - record = [] - for i in range(len(listdata) - threshold - 1): - if is_continuous_sublist( - listdata[i + 1 : i + threshold + 2], listdata[: i + threshold + 1] - ): - record.extend(list(range(i + 1, i + threshold + 2))) - reco_last = list(set(record)) - reco_last.sort() - processed_data = [ - listdata[j] for j in range(len(listdata)) if j not in reco_last - ] - return processed_data, reco_last - - -def remove_duplicate_subsequences(arr, threshold=3): - """ - 处理二维数组版本,将每行视为独立元素,避免内存溢出 - :param arr: 二维np数组,形状为(N, D) - :param threshold: 需要删除的连续重复子序列最小长度 - :return: 去重后的数组,被删除的索引列表 - """ - if arr.ndim != 2: - raise ValueError("输入必须是二维数组") - - n = arr.shape[0] - print(arr[9]) - # 哈希化每行以便快速比较 - dtype = np.dtype((np.void, arr.dtype.itemsize * arr.shape[1])) - hashed_arr = np.ascontiguousarray(arr).view(dtype).flatten() - - # 初始化滚动数组和列最大值记录 - prev_row = np.zeros(n, dtype=int) - max_per_col = np.zeros(n, dtype=int) - targ = 10 - for i in range(n): - # 生成当前行比较掩码 - equal_mask = hashed_arr[i] == hashed_arr - - # 计算当前行DP值 - curr_row = np.zeros(n, dtype=int) - curr_row[0] = equal_mask[0] # 处理j=0 - - if i > 0: - # 向量化计算j>=1的情况 - curr_row[1:] = np.where(equal_mask[1:], prev_row[:-1] + 1, 0) - - # 更新列最大值(只考虑i<=j的情况) - col_mask = np.arange(n) > i - max_per_col = np.maximum(max_per_col, curr_row * col_mask) - - # 滚动更新 - prev_row = curr_row - if i * 100 / n >= targ: - print("检查重复数据,处理进度: {:.1f}%: ".format(i * 100 / n)) - targ += 10 - - # 确定有效列并生成删除索引 - valid_cols = np.where(max_per_col >= threshold)[0] - to_remove = set() - - for j in valid_cols: - length = max_per_col[j] - start = max(0, j - length) - to_remove.update(range(start, j + 1)) - - final_indices = sorted(to_remove) - return np.delete(arr, final_indices, axis=0), final_indices - - -def isfloat(value): - try: - float(value) - return True - except ValueError: - return False - - -def read_and_remove_zeros(filename, MONSTER_NUM=56): - """ - 输入数据文件名 - 输出去0和空之后的数组,和删去的行号列表 - """ - data = [] - datafull = [] - row_id = 0 - kong = [] - short = [] - lines_num = MONSTER_NUM * 2 - with open(filename, "r") as file: - csv_reader = csv.reader(file) - for row in csv_reader: - if len(row) < lines_num + 1: - short.append(row_id) - data.append([0] * lines_num) - datafull.append([0] * lines_num) - # 分离数字部分和末尾的字母 - elif isfloat(row[0]) and "" not in row[:lines_num]: - numbers = list( - map(int, map(float, row[:lines_num])) - ) # 转换为整数列表 - vals = row[lines_num:] - datafull.append(numbers + vals) - data.append(numbers) - else: - kong.append(row_id) - data.append([0] * lines_num) - datafull.append([0] * lines_num) - # 把这一行转化为全0行暂时录入 - row_id += 1 - print("原数据总长度:", len(datafull)) - print("数据长度过短的行:", merge(short)) - print("含有不合法数据的行:", merge(kong)) - - np_array = np.array(data) - # 去除全零行 - all_zeros = [] - for i in range(np_array.shape[0]): - if np.all(np_array[i][MONSTER_NUM:] == 0) or np.all( - np_array[i][:MONSTER_NUM] == 0 - ): - all_zeros.append(i) - # np.delete(np_array, all_zeros, axis=0) - - data_new = [ - datafull[j] for j in range(len(datafull)) if j not in all_zeros - ] - - all_zeros_idx = [i for i in all_zeros if i not in kong + short] - print("一侧数据全为0的行:", merge(all_zeros_idx)) - print("筛选后数据总长度:", len(data_new)) - return data_new, all_zeros, len(datafull) - - -def do_duplicate(listdata): - if listdata == []: - return [], [] - num_data = [ - list(map(int, map(float, i[: MONSTER_NUM * 2]))) for i in listdata - ] - np_num_data = np.array(num_data) - _, remove_list = remove_duplicate_subsequences(np_num_data, threshold=3) - # print(remove_list) - result = [ - listdata[j] for j in range(len(listdata)) if j not in remove_list - ] - return result, remove_list - - -def ori_pos(n, del1, del2): - remaining_after_first = [i for i in range(n) if i not in del1] - second_deleted_original = [remaining_after_first[t] for t in del2] - all_deleted = del1 + second_deleted_original - all_deleted.sort() - return all_deleted, second_deleted_original - - -def view_monster_counts(listdata): - if listdata == []: - return True, [], [] - wrong_counts = [] - num_left = [list(map(int, map(float, i[:MONSTER_NUM]))) for i in listdata] - num_right = [ - list(map(int, map(float, i[MONSTER_NUM : MONSTER_NUM * 2]))) - for i in listdata - ] - print(len(num_left[0]), len(num_right[0])) - black_listed = False - MONSTER_MIN = 0 - MONSTER_MAX = 100 - MONSTER_LIMIT = { - 0: [range(0, 100), "狗", False], - 1: [range(0, 50), "红虫", False], - 2: [range(0, 30), "大盾", False], - 3: [range(0, 30), "大剑", False], - 6: [range(0, 6), "庞贝", False], - 8: [range(0, 4), "石头人", False], - 27: [range(0, 4), "杰斯顿", False], - 28: [[0], "自在", True], - 29: [[0], "狼主", True], - 30: [[0], "雷德", True], # 三大boss全设为0 - } - ind = 0 - for i1, i2 in zip(num_left, num_right): - for x1 in i1: - if x1 < MONSTER_MIN: - print(f"{ind}行左侧发现小于0的数据!") - if ind not in wrong_counts: - wrong_counts.append(ind) - if x1 > MONSTER_MAX: - print(f"{ind}行左侧发现大于100的数据!") - if ind not in wrong_counts: - wrong_counts.append(ind) - for x2 in i2: - if x2 < MONSTER_MIN: - print(f"{ind}行右侧发现小于0的数据!") - if ind not in wrong_counts: - wrong_counts.append(ind) - if x2 > MONSTER_MAX: - print(f"{ind}行右侧发现大于100的数据!") - if ind not in wrong_counts: - wrong_counts.append(ind) - for j in MONSTER_LIMIT: - if i1[j] not in MONSTER_LIMIT[j][0]: - print(f"{ind}行左侧发现{MONSTER_LIMIT[j][1]},数量:{i1[j]}") - if ind not in wrong_counts: - wrong_counts.append(ind) - if MONSTER_LIMIT[j][2] and not black_listed: - black_listed = True - print(f"确认为30人数据,文档加入黑名单。") - if i2[j] not in MONSTER_LIMIT[j][0]: - print(f"{ind}行右侧发现{MONSTER_LIMIT[j][1]},数量:{i2[j]}") - if ind not in wrong_counts: - wrong_counts.append(ind) - if MONSTER_LIMIT[j][2] and not black_listed: - black_listed = True - print(f"确认为30人数据,文档加入黑名单。") - ind += 1 - processed_data = [ - listdata[j] for j in range(len(listdata)) if j not in wrong_counts - ] - mwdata = is_list_true_np(processed_data) - processed_data2 = [ - processed_data[j] - for j in range(len(processed_data)) - if j not in mwdata - ] - print(f"怪物信息不符合权重分配的数据行:{mwdata}") - return black_listed, wrong_counts, mwdata, processed_data2 - - -def del_duplicate_by_time(listdata, delete_no_time=True): - ind = 0 - no_time = [] - timedata = [] - wrong_time = [] - for i in listdata: - if len(i) < MONSTER_NUM * 2 + 2 or i[-1] == "N/A": - no_time.append(ind) - ind += 1 - print(merge(no_time), "行:未发现时间戳!") - data_with_time = [ - listdata[j] for j in range(len(listdata)) if j not in no_time - ] - ind = 0 - for i in data_with_time: - if i[-1] not in timedata: - timedata.append(i[-1]) - else: - wrong_time.append(ind) - print( - f"{timedata.index(i[-1])}行与{ind}行发现同名截图,文件名:{i[-1]}" - ) - ind += 1 - if not delete_no_time: - # 先找到wrongtime元素的原始位置,再从原列表删除 - remaining_after_first = [ - i for i in range(len(listdata)) if i not in no_time - ] - second_deleted_original = [ - remaining_after_first[t] for t in wrong_time - ] - data_with_time_ok = [ - listdata[j] - for j in range(len(listdata)) - if j not in second_deleted_original - ] - wrong_time = second_deleted_original - else: - data_with_time_ok = [ - data_with_time[j] - for j in range(len(data_with_time)) - if j not in wrong_time - ] - return data_with_time_ok, no_time, wrong_time - - -def savecsv(listdata, outputfile): - # 处理数字转换 - processed = [] - for row in listdata: - new_row = [] - for item in row: - if isinstance(item, (int, float)): - new_row.append(int(item)) - else: - new_row.append(item) - processed.append(new_row) - - # 写入CSV文件 - with open(outputfile, "w", newline="") as f: - csv.writer(f).writerows(processed) - print(f"已保存至{outputfile}") - - -def find_csv_files(root_dir): - csv_files = [] - for root, dirs, files in os.walk(root_dir): - for file in files: - if file.lower().endswith(".csv"): - csv_path = os.path.join(root, file) - csv_files.append(csv_path) - return csv_files - - -def easydata2data(easydata): - # 测试用函数 - # easydata格式:[[[序号,数量][序号,数量][序号,数量]],[[序号,数量][序号,数量][序号,数量]],结果],没有的序号和数量留-1,序号是真实序号 - datalist = [0] * MONSTER_NUM * 2 - for i in easydata[0]: - if i[0] > 0: - datalist[i[0] - 1] = i[1] - for i in easydata[1]: - if i[0] > 0: - datalist[i[0] - 1 + MONSTER_NUM] = i[1] - datalist.extend(easydata[2]) - return datalist - - -def find_where_from(easydata, floder_path): - # easydata格式:[[[序号,数量][序号,数量][序号,数量]],[[序号,数量][序号,数量][序号,数量]],结果],没有的序号和数量留-1,序号是真实序号 - datalist = [0] * MONSTER_NUM * 2 - from_list = [] - for i in easydata[0]: - if i[0] > 0: - datalist[i[0] - 1] = i[1] - for i in easydata[1]: - if i[0] > 0: - datalist[i[0] - 1 + MONSTER_NUM] = i[1] - datalist.extend(easydata[2]) - print(datalist) - csvlist = find_csv_files(floder_path) - for c in csvlist: - print(c) - for c in csvlist: - print(f"正在检查:{c}…………………………") - lines_num = MONSTER_NUM * 2 - datafull = [] - row_id = 0 - with open(c, "r") as file: - csv_reader = csv.reader(file) - for row in csv_reader: - if ( - isfloat(row[0]) - and "" not in row[:lines_num] - and len(row) > lines_num - ): - numbers = list( - map(int, map(float, row[:lines_num])) - ) # 转换为整数列表 - vals = [row[lines_num]] - if datalist == numbers + vals: - from_list.append([c, row_id + 1]) - print(f"数据来源于:{c},第{row_id+1}行!") - row_id += 1 - str_from = "" - if from_list != []: - str_from = "\n".join( - [i[0] + "第:" + str(i[1]) + "行" for i in from_list] - ) - print(f"可能的数据来源:{str_from}") - - -def is_distance_not_over_60(a, b, c, d): - # a, b = interval1 - # c, d = interval2 - # 判断区间是否有交集 - if max(a, c) <= min(b, d): - return True # 有交集时距离为0,未超过60 - # 计算不重叠时的间隔 - if b < c: - distance = c - b # interval1在左,interval2在右 - else: - distance = a - d # interval2在左,interval1在右 - return distance <= 60 - - -def is_list_true_np(fulllist): - cost_list = [ - [2, 0], - [2, 0.1], - [7, 0], - [7, 0], - [3, 0.1], - [10, 0], - [25, 15], - [22, 0], - [25, 100], - [7, 0], - [5, 0.2], - [7, 0], - [2, 0], - [15, 2], - [13, 0], - [12, 1.5], - [6, 0.2], - [18, 3], - [15, 3], - [18, 1], - [11, 0], - [10, 1], - [16, 0], - [5, 0.5], - [15, 2], - [14, 0], - [30, 0], - [35, 100], - [-1, -1], - [-1, -1], - [-1, -1], - [6, 0.5], - [6, 0], - [16, 0], - [15, 5], - [11, 0], - [26, 0], - [15, 0], - [4, 0.1], - [10, 0], - [21, 0], - [5, 0.2], - [18, 0], - [9, 1.5], - [8, 0.5], - [16, 0], - [21, 0], - [7, 0], - [36, 10], - [10, 2], - [30, 15], - [25, 0], - [27, 0], - [32, 6], - [25, 50], - [15, 5], - ] - round_cost_list = [ - [50, 70], - [70, 90], - [90, 110], - [110, 130], - [120, 160], - [140, 180], - [160, 200], - [170, 230], - [190, 250], - [210, 270], - ] - - # Convert to numpy arrays - cost_arr = np.array(cost_list) - round_cost_arr = np.array(round_cost_list) - round_low = round_cost_arr[:, 0] - round_high = round_cost_arr[:, 1] - - # Validity mask for cost entries not equal to [-1, -1] - valid_mask = np.all(cost_arr != [-1, -1], axis=1) - - # Split the input into left and right parts - fulllist_np = np.array([i[:112] for i in fulllist], dtype=np.float64) - N = fulllist_np.shape[0] - left_part = fulllist_np[:, :56] - right_part = fulllist_np[:, 56:112] - - # Calculate valid entries (cost not [-1,-1] and count >0) - valid_left = valid_mask[np.newaxis, :] & (left_part > 0) - valid_right = valid_mask[np.newaxis, :] & (right_part > 0) - - # Compute mincostL and maxcostL for left - left_min_terms = ( - (left_part - 1) * cost_arr[np.newaxis, :, 0] - + ((left_part - 1) * (left_part - 2) * cost_arr[np.newaxis, :, 1]) / 2 - + 0.01 - ) * valid_left - mincostL = left_min_terms.sum(axis=1) - - left_max_terms = ( - (left_part + 1) * cost_arr[np.newaxis, :, 0] - + (left_part * (left_part + 1) * cost_arr[np.newaxis, :, 1]) / 2 - - 0.01 - ) * valid_left - maxcostL = left_max_terms.sum(axis=1) - - # Compute mincostR and maxcostR for right - right_min_terms = ( - (right_part - 1) * cost_arr[np.newaxis, :, 0] - + ((right_part - 1) * (right_part - 2) * cost_arr[np.newaxis, :, 1]) - / 2 - + 0.01 - ) * valid_right - mincostR = right_min_terms.sum(axis=1) - - right_max_terms = ( - (right_part + 1) * cost_arr[np.newaxis, :, 0] - + (right_part * (right_part + 1) * cost_arr[np.newaxis, :, 1]) / 2 - - 0.01 - ) * valid_right - maxcostR = right_max_terms.sum(axis=1) - - # Check overlap with round costs - left_low = np.maximum(mincostL[:, np.newaxis], round_low) - left_high = np.minimum(maxcostL[:, np.newaxis], round_high) - left_cond = left_low <= left_high - - right_low = np.maximum(mincostR[:, np.newaxis], round_low) - right_high = np.minimum(maxcostR[:, np.newaxis], round_high) - right_cond = right_low <= right_high - - both_cond = left_cond & right_cond - any_round = np.any(both_cond, axis=1) - - # Get indices where no round condition is satisfied - false_indices = np.where(~any_round)[0].tolist() - return false_indices - - -def is_list_true(onelist): - roundlist = [] - # 费用和附加费用,写死在代码里吧,不想读文件了。 - cost_list = [ - [2, 0], - [2, 0.1], - [7, 0], - [7, 0], - [3, 0.1], - [10, 0], - [25, 15], - [22, 0], - [25, 100], - [7, 0], - [5, 0.2], - [7, 0], - [2, 0], - [15, 2], - [13, 0], - [12, 1.5], - [6, 0.2], - [18, 3], - [15, 3], - [18, 1], - [11, 0], - [10, 1], - [16, 0], - [5, 0.5], - [15, 2], - [14, 0], - [30, 0], - [35, 100], - [-1, -1], - [-1, -1], - [-1, -1], - [6, 0.5], - [6, 0], - [16, 0], - [15, 5], - [11, 0], - [26, 0], - [15, 0], - [4, 0.1], - [10, 0], - [21, 0], - [5, 0.2], - [18, 0], - [9, 1.5], - [8, 0.5], - [16, 0], - [21, 0], - [7, 0], - [36, 10], - [10, 2], - [30, 15], - [25, 0], - [27, 0], - [32, 6], - [25, 50], - [15, 5], - ] - # - round_cost_list = [ - [50, 70], - [70, 90], - [90, 110], - [110, 130], - [120, 160], - [140, 180], - [160, 200], - [170, 230], - [190, 250], - [210, 270], - ] - left = onelist[:MONSTER_NUM] - right = onelist[MONSTER_NUM : MONSTER_NUM * 2] - # result = onelist[MONSTER_NUM*2] - # print(left,right,result) - mincostL = sum( - [ - (left[i] - 1) * cost_list[i][0] - + (left[i] - 1) * (left[i] - 2) * cost_list[i][1] / 2 - + 0.01 - for i in range(len(left)) - if (cost_list[i] != [-1, -1]) and (left[i] > 0) - ] - ) - maxcostL = sum( - [ - (left[i] + 1) * cost_list[i][0] - + left[i] * (left[i] + 1) * cost_list[i][1] / 2 - - 0.01 - for i in range(len(left)) - if (cost_list[i] != [-1, -1]) and (left[i] > 0) - ] - ) - mincostR = sum( - [ - (right[i] - 1) * cost_list[i][0] - + (right[i] - 1) * (right[i] - 2) * cost_list[i][1] / 2 - + 0.01 - for i in range(len(right)) - if (cost_list[i] != [-1, -1]) and (right[i] > 0) - ] - ) - maxcostR = sum( - [ - (right[i] + 1) * cost_list[i][0] - + right[i] * (right[i] + 1) * cost_list[i][1] / 2 - - 0.01 - for i in range(len(right)) - if (cost_list[i] != [-1, -1]) and (right[i] > 0) - ] - ) - print(mincostL, maxcostL, mincostR, maxcostR) - for i in range(len(round_cost_list)): - if max(mincostL, round_cost_list[i][0]) <= min( - maxcostL, round_cost_list[i][1] - ) and max(mincostR, round_cost_list[i][0]) <= min( - maxcostR, round_cost_list[i][1] - ): - roundlist.append(i) - if roundlist != []: - return True - else: - print(f"{onelist}is not true!!!") - return False - # return is_distance_not_over_60(mincostL,maxcostL,mincostR,maxcostR) - - -def recognize_review( - data, img_floder, matched_threshold=0.1, ocr_threshold=0.5 -): - print("正在进行识别数据检查") - print("data行数:", len(data)) - ref_row = [0] * (recognize.MONSTER_COUNT * 2) - need_delete = [False] * len(data) - for idx, row in tqdm.tqdm( - enumerate(data), total=len(data), desc="Processing rows" - ): - ref_row = [0] * (recognize.MONSTER_COUNT * 2) - try: - img_name = row[recognize.MONSTER_COUNT * 2 + 1] - img_path = img_floder / Path(img_name) - if not img_path.exists(): - print(f"未找到对应的图像: {img_name} ") - continue - img = cv2.imread(img_path) - main_roi = ((0, 0), (img.shape[1], img.shape[0])) - results = recognize.process_regions( - main_roi, img, matched_threshold, ocr_threshold - ) - # 处理结果 - for res in results: - if "error" in res: - print( - f"识别失败 行号: {idx}, 图片: {img_name}, 错误类型: {res['error']}", - file=sys.stderr, - ) - break - if res["matched_id"]: - if res["region_id"] < 3: - ref_row[res["matched_id"] - 1] = int(res["number"]) - else: - ref_row[res["matched_id"] - 1 + MONSTER_NUM] = int( - res["number"] - ) - else: - # 检查数据行是否与参考行匹配 - data_row = row[0 : recognize.MONSTER_COUNT * 2] - if data_row != ref_row: - print( - f"找到不匹配的数据行: {idx} 行,对应图片文件: {img_name}", - file=sys.stderr, - ) - print(f"识别结果 : {ref_row}", file=sys.stderr) - print(f"文件数据 : {data_row}", file=sys.stderr) - need_delete[idx] = True - else: - need_delete[idx] = False - except Exception as e: - logging.exception(f"Error processing line {idx}", e) - need_delete[idx] = True - newdata = [row for idx, row in enumerate(data) if not need_delete[idx]] - deleted = [idx for idx, del_flag in enumerate(need_delete) if del_flag] - return newdata, deleted - - -# newdata,deleted,ori_len = read_and_remove_zeros('0502.csv',MONSTER_NUM=56) -# _,inc = remove_duplicate_subsequences() -# print('数据例:',newdata[:3]) -# result,deleted2 = do_duplicate(newdata) -# print(deleted,deleted2) -# dt = ori_pos(ori_len,deleted,deleted2) -# print(dt) -# print('筛选后数据总长度:',len(result)) -# view_monster_counts(newdata) -# del_duplicate_by_time(newdata) - - -def process_full( - filename, - do_remove_duplicate_subsequences=False, - delete_no_time=True, - open_black_list=True, - re_recognize_imgs=False, - img_floder="", - matched_threshold=0.1, - ocr_threshold=0.5, -): - wrong_type_list = [] - newdata, deleted0, ori_len = read_and_remove_zeros( - filename, MONSTER_NUM=56 - ) - deleted1 = [] - if do_remove_duplicate_subsequences: - newdata, deleted1 = do_duplicate(newdata) - newdata, deleted2, deleted3 = del_duplicate_by_time( - newdata, delete_no_time - ) - if not delete_no_time: - deleted2 = [] - black_listed, deleted4, deleted5, newdata = view_monster_counts(newdata) - deleted6 = [] - if re_recognize_imgs: - newdata, deleted6 = recognize_review( - newdata, img_floder, matched_threshold, ocr_threshold - ) - deleted7 = [] - if open_black_list: - newdata, deleted7 = process_black_list(newdata) - - dl = deleted0 - flag = 0 - for i in [ - deleted1, - deleted2, - deleted3, - deleted4, - deleted5, - deleted6, - deleted7, - ]: - if i != []: - dl, secori = ori_pos(ori_len, dl, i) - if flag == 0: - wrong_type_list.append( - ["不合法的数据:", merge([i + 1 for i in deleted0])] - ) - wrong_type_list.append( - ["重复出现的连续数据*:", merge([i + 1 for i in secori])] - ) - elif flag == 1: - wrong_type_list.append( - ["未包含时间轴的数据:", merge([i + 1 for i in secori])] - ) - elif flag == 2: - wrong_type_list.append( - ["时间轴信息重复的数据:", merge([i + 1 for i in secori])] - ) - elif flag == 3: - wrong_type_list.append( - ["怪物信息错误的数据:", merge([i + 1 for i in secori])] - ) - elif flag == 4: - wrong_type_list.append( - [ - "不符合出怪权重规则的数据:", - merge([i + 1 for i in secori]), - ] - ) - elif flag == 5: - wrong_type_list.append( - ["经图片识别错误的数据*:", merge([i + 1 for i in secori])] - ) - elif flag == 6: - wrong_type_list.append( - ["黑名单内数据:", merge([i + 1 for i in secori])] - ) - flag += 1 - return black_listed, newdata, dl, wrong_type_list - - -def test1(): - black_listed, newdata, dl, wrong_type_list = process_full( - "0502processed.csv" - ) - dllist = [i + 1 for i in dl] - print(f"删除了{dllist}行的数据") - for i in wrong_type_list: - print(i) - savecsv(newdata, "0502processed2.csv") - - -def process_floder( - flodername, - savefilename, - lastsavefilename, - do_remove_duplicate_subsequences=True, - delete_no_time=True, - open_black_list=True, - re_recognize_imgs=False, - img_floder="", - matched_threshold=0.1, - ocr_threshold=0.5, -): - """ - 输入: - flodername:需要处理的文件夹名 - savefilename:全部整合保存到的文件名(不进行总去重) - lastsavefilename:全部整合并去重保存到的最终文件名 - do_remove_duplicate_subsequences:是否清理连续3个以上重复元素的重复序列 - delete_no_time:是否删除没有时间戳的数据行 - """ - global black_list_rows - full_data_list = [] - csvlist = find_csv_files(flodername) - for csv in csvlist: - print(csv) - for csv in csvlist: - print(f"正在处理:{csv}…………………………") - black_listed, newdata, dl, wrong_type_list = process_full( - csv, - do_remove_duplicate_subsequences, - delete_no_time, - open_black_list, - re_recognize_imgs, - img_floder, - matched_threshold, - ocr_threshold, - ) - dllist = [i + 1 for i in dl] - print(f"删除了{merge(dllist)}行的数据") - for i in wrong_type_list: - print(i) - if not black_listed: - # 未进黑名单则合并至全部数据 - full_data_list += newdata - else: - print(f"该数据为30人局数据,自动进入黑名单,不计入总数据!") - if len(newdata) < 5000: # 不是整合数据 - black_list_rows += newdata - savecsv(full_data_list, savefilename) - black_listed, newdata, dl, wrong_type_list = process_full( - savefilename, - do_remove_duplicate_subsequences, - delete_no_time, - open_black_list, - re_recognize_imgs, - img_floder, - matched_threshold, - ocr_threshold, - ) - # 保存后再总处理去重 - dllist = [i + 1 for i in dl] - print(f"删除了{merge(dllist)}行的数据") - for i in wrong_type_list: - print(i) - savecsv(newdata, lastsavefilename) - - -def process_black_list(full_data): - # 黑名单里所有的数据检测到重复的就删 - global black_list_rows - delete_rows = [] - ok_data = [] - idx = 0 - for i in full_data: - if i in black_list_rows: - delete_rows.append(idx) - else: - ok_data.append(i) - idx += 1 - print(f"黑名单内数据:{merge(delete_rows)}") - return ok_data, delete_rows - - -def process_file( - filename, - savefilename, - do_remove_duplicate_subsequences=True, - delete_no_time=True, - open_black_list=True, - re_recognize_imgs=False, - img_floder="", - matched_threshold=0.1, - ocr_threshold=0.5, -): - """ - 输入: - filename:需要处理的文件名 - savefilename:处理后保存到的文件名 - do_remove_duplicate_subsequences:是否清理连续3个以上重复元素的重复序列 - delete_no_time:是否删除没有时间戳的数据行 - """ - black_listed, newdata, dl, wrong_type_list = process_full( - filename, - do_remove_duplicate_subsequences, - delete_no_time, - open_black_list, - re_recognize_imgs, - img_floder, - matched_threshold, - ocr_threshold, - ) - # 保存后再总处理去重 - dllist = [i + 1 for i in dl] - print(f"删除了{merge(dllist)}行的数据") - for i in wrong_type_list: - print(i) - savecsv(newdata, savefilename) - - -# process_floder(r'D:\Backup\Downloads\arcdata','arcdata_fullaa.csv','arcdata_full_washed_plus.csv') - -import tkinter as tk -from tkinter import ttk, filedialog, messagebox -import sys -import threading -import queue - - -class RedirectText(object): - def __init__(self, text_widget, log_file="processing.log"): - self.text_widget = text_widget - self.log_file = log_file - self.queue = queue.Queue() - self.root = text_widget.master - self.lock = threading.Lock() - - # 初始化日志文件 - self.setup_logfile() - - def setup_logfile(self): - try: - # 使用追加模式打开日志文件 - self.log_fd = open(self.log_file, "a", encoding="utf-8") - except Exception as e: - self.log_fd = None - self.write(f"无法打开日志文件: {str(e)}\n") - - def write(self, message): - # 写入日志文件(带线程锁) - with self.lock: - if self.log_fd: - try: - self.log_fd.write(message) - self.log_fd.flush() # 确保立即写入磁盘 - except Exception as e: - self.log_fd = None - self.queue.put(f"日志写入失败: {str(e)}\n") - - # 写入队列供界面显示 - self.queue.put(message) - self.root.after(100, self.update_text) - - def update_text(self): - while not self.queue.empty(): - msg = self.queue.get_nowait() - self.text_widget.insert(tk.END, msg) - self.text_widget.see(tk.END) - - def flush(self): - pass - - def close_logfile(self): - with self.lock: - if self.log_fd: - self.log_fd.close() - self.log_fd = None - - -class ProcessingThread(threading.Thread): - def __init__(self, func, args=(), kwargs={}, callback=None): - super().__init__() - self.func = func - self.args = args - self.kwargs = kwargs - self.callback = callback - self.daemon = True - self.exception = None - - def run(self): - try: - self.func(*self.args, **self.kwargs) - except Exception as e: - self.exception = e - finally: - if self.callback: - self.callback(self.exception) - - -def create_gui(): - root = tk.Tk() - root.title("数据搅拌机") - root.geometry("800x600") - - # 在此处定义关闭事件处理函数(推荐位置) - def on_close(): - sys.stdout.close_logfile() # 关闭日志文件 - if messagebox.askokcancel( - "退出", "确定要退出程序吗?" - ): # 添加确认对话框 - root.destroy() # 销毁窗口 - - # 绑定关闭事件处理 - root.protocol("WM_DELETE_WINDOW", on_close) - - # 创建文本输出区域 - output_text = tk.Text(root, wrap=tk.WORD) - output_text.grid( - row=3, column=0, columnspan=2, padx=10, pady=10, sticky="nsew" - ) - sys.stdout = RedirectText(output_text, "data_processing.log") - - # 处理文件夹的Frame - folder_frame = ttk.LabelFrame( - root, - text="处理文件夹(处理文件夹及其所有子文件夹下的CSV文件,并合并为一个)", - ) - folder_frame.grid(row=0, column=0, padx=10, pady=5, sticky="ew") - - # 处理文件夹的组件 - ttk.Label(folder_frame, text="选择文件夹:").grid( - row=0, column=0, padx=5, sticky="w" - ) - folder_path = tk.StringVar() - folder_entry = ttk.Entry(folder_frame, textvariable=folder_path, width=40) - folder_entry.grid(row=0, column=1, padx=5) - ttk.Button( - folder_frame, - text="浏览", - command=lambda: folder_path.set(filedialog.askdirectory()), - ).grid(row=0, column=2, padx=5) - - ttk.Label(folder_frame, text="中间保存文件(不进行最终去重):").grid( - row=1, column=0, padx=5, sticky="w" - ) - interim_save = tk.StringVar() - ttk.Entry(folder_frame, textvariable=interim_save, width=40).grid( - row=1, column=1, padx=5 - ) - ttk.Button( - folder_frame, - text="浏览", - command=lambda: interim_save.set( - filedialog.asksaveasfilename( - defaultextension=".csv", - filetypes=[("CSV文件", "*.csv"), ("所有文件", "*.*")], - ) - ), - ).grid(row=1, column=2, padx=5) - - ttk.Label(folder_frame, text="最终保存文件:").grid( - row=2, column=0, padx=5, sticky="w" - ) - final_save = tk.StringVar() - ttk.Entry(folder_frame, textvariable=final_save, width=40).grid( - row=2, column=1, padx=5 - ) - ttk.Button( - folder_frame, - text="浏览", - command=lambda: final_save.set( - filedialog.asksaveasfilename( - defaultextension=".csv", - filetypes=[("CSV文件", "*.csv"), ("所有文件", "*.*")], - ) - ), - ).grid(row=2, column=2, padx=5) - - # 复选框 - remove_dup = tk.BooleanVar(value=False) - ttk.Checkbutton( - folder_frame, - text="不依赖时间戳清理重复子序列(在大数据集会非常慢,通常关闭)", - variable=remove_dup, - ).grid(row=3, column=0, columnspan=3, sticky="w") - - del_time = tk.BooleanVar(value=True) - ttk.Checkbutton( - folder_frame, text="删除无时间戳数据", variable=del_time - ).grid(row=4, column=0, columnspan=3, sticky="w") - - open_black = tk.BooleanVar(value=True) - ttk.Checkbutton( - folder_frame, - text="将黑名单文件内的所有数据行同时加入黑名单", - variable=open_black, - ).grid(row=5, column=0, columnspan=3, sticky="w") - - # 修改后的图片识别行(将复选框和阈值输入放在同一行) - re_recognize_var = tk.BooleanVar(value=False) - ttk.Checkbutton( - folder_frame, - text="启用图片二次识别(必须指定图片路径)", - variable=re_recognize_var, - ).grid(row=6, column=0, padx=5, sticky="w") - - # 添加匹配阈值设置 - ttk.Label(folder_frame, text="匹配阈值:").grid( - row=6, column=1, padx=(20, 5), sticky="e" - ) - matched_threshold_var = tk.DoubleVar(value=0.1) - ttk.Entry(folder_frame, textvariable=matched_threshold_var, width=6).grid( - row=6, column=2, sticky="w" - ) - - # 添加OCR阈值设置 - ttk.Label(folder_frame, text="OCR阈值:").grid( - row=6, column=3, padx=(20, 5), sticky="e" - ) - ocr_threshold_var = tk.DoubleVar(value=0.5) - ttk.Entry(folder_frame, textvariable=ocr_threshold_var, width=6).grid( - row=6, column=4, sticky="w" - ) - - # 调整后续行号(原row=6改为row=7开始) - ttk.Label(folder_frame, text="图片文件夹路径:").grid( - row=7, column=0, padx=5, sticky="w" - ) - img_folder_path = tk.StringVar() - ttk.Entry(folder_frame, textvariable=img_folder_path, width=40).grid( - row=7, column=1, padx=5 - ) - ttk.Button( - folder_frame, - text="浏览", - command=lambda: img_folder_path.set(filedialog.askdirectory()), - ).grid(row=7, column=2, padx=5) - - # 调整处理文件夹按钮的行号 - - # 处理文件夹按钮 - folder_button = ttk.Button(folder_frame, text="执行处理") - folder_button.grid(row=8, column=0, columnspan=5, pady=5) - - # 处理文件的Frame - file_frame = ttk.LabelFrame(root, text="处理单个文件") - file_frame.grid(row=1, column=0, padx=10, pady=5, sticky="ew") - - # 处理文件的组件 - ttk.Label(file_frame, text="选择文件:").grid( - row=0, column=0, padx=5, sticky="w" - ) - file_path = tk.StringVar() - ttk.Entry(file_frame, textvariable=file_path, width=40).grid( - row=0, column=1, padx=5 - ) - ttk.Button( - file_frame, - text="浏览", - command=lambda: file_path.set(filedialog.askopenfilename()), - ).grid(row=0, column=2, padx=5) - - ttk.Label(file_frame, text="保存路径:").grid( - row=1, column=0, padx=5, sticky="w" - ) - save_path = tk.StringVar() - ttk.Entry(file_frame, textvariable=save_path, width=40).grid( - row=1, column=1, padx=5 - ) - ttk.Button( - file_frame, - text="浏览", - command=lambda: save_path.set( - filedialog.asksaveasfilename( - defaultextension=".csv", - filetypes=[("CSV文件", "*.csv"), ("所有文件", "*.*")], - ) - ), - ).grid(row=1, column=2, padx=5) - - # 复选框 - remove_dup_file = tk.BooleanVar(value=False) - ttk.Checkbutton( - file_frame, - text="不依赖时间戳清理重复子序列(在大数据集会非常慢,通常关闭)", - variable=remove_dup_file, - ).grid(row=2, column=0, columnspan=3, sticky="w") - - del_time_file = tk.BooleanVar(value=True) - ttk.Checkbutton( - file_frame, text="删除无时间戳数据", variable=del_time_file - ).grid(row=3, column=0, columnspan=3, sticky="w") - - open_black_file = tk.BooleanVar(value=True) - ttk.Checkbutton( - file_frame, - text="将黑名单文件内的所有数据行同时加入黑名单", - variable=open_black_file, - ).grid(row=4, column=0, columnspan=3, sticky="w") - - # 修改后的图片识别行 - re_recognize_file_var = tk.BooleanVar(value=False) - ttk.Checkbutton( - file_frame, - text="启用图片二次识别(必须指定图片路径)", - variable=re_recognize_file_var, - ).grid(row=5, column=0, padx=5, sticky="w") - - # 匹配阈值 - ttk.Label(file_frame, text="匹配阈值:").grid( - row=5, column=1, padx=(20, 5), sticky="e" - ) - matched_threshold_file_var = tk.DoubleVar(value=0.1) - ttk.Entry( - file_frame, textvariable=matched_threshold_file_var, width=6 - ).grid(row=5, column=2, sticky="w") - - # OCR阈值 - ttk.Label(file_frame, text="OCR阈值:").grid( - row=5, column=3, padx=(20, 5), sticky="e" - ) - ocr_threshold_file_var = tk.DoubleVar(value=0.5) - ttk.Entry(file_frame, textvariable=ocr_threshold_file_var, width=6).grid( - row=5, column=4, sticky="w" - ) - - # 调整后续行号 - ttk.Label(file_frame, text="图片文件夹路径:").grid( - row=6, column=0, padx=5, sticky="w" - ) - img_folder_file_path = tk.StringVar() - ttk.Entry(file_frame, textvariable=img_folder_file_path, width=40).grid( - row=6, column=1, padx=5 - ) - ttk.Button( - file_frame, - text="浏览", - command=lambda: img_folder_file_path.set(filedialog.askdirectory()), - ).grid(row=6, column=2, padx=5) - - # 调整处理文件按钮的行号 - - # 处理文件按钮 - file_button = ttk.Button(file_frame, text="执行处理") - file_button.grid(row=7, column=0, columnspan=5, pady=5) - - # 配置网格权重 - root.grid_rowconfigure(3, weight=1) - root.grid_columnconfigure(0, weight=1) - - # 按钮回调函数 - def process_folder_wrapper(): - folder = folder_path.get() - interim = interim_save.get() - final = final_save.get() - if not folder or not interim or not final: - messagebox.showerror("错误", "请填写所有路径") - return - - folder_button.config(state=tk.DISABLED) - - def callback(e): - folder_button.config(state=tk.NORMAL) - if e: - messagebox.showerror("错误", str(e)) - else: - messagebox.showinfo("完成", "文件夹处理完成") - - thread = ProcessingThread( - func=process_floder, - args=( - folder, - interim, - final, - remove_dup.get(), - del_time.get(), - open_black.get(), - re_recognize_var.get(), - Path(img_folder_path.get()), - matched_threshold_var.get(), - ocr_threshold_var.get(), - ), - callback=callback, - ) - thread.start() - - def process_file_wrapper(): - input_file = file_path.get() - output_file = save_path.get() - if not input_file or not output_file: - messagebox.showerror("错误", "请填写所有路径") - return - - file_button.config(state=tk.DISABLED) - - def callback(e): - file_button.config(state=tk.NORMAL) - if e: - messagebox.showerror("错误", str(e)) - else: - messagebox.showinfo("完成", "文件处理完成") - - thread = ProcessingThread( - func=process_file, - args=( - input_file, - output_file, - remove_dup_file.get(), - del_time_file.get(), - open_black_file.get(), - re_recognize_file_var.get(), - Path(img_folder_file_path.get()), - matched_threshold_file_var.get(), - ocr_threshold_file_var.get(), - ), - callback=callback, - ) - thread.start() - - # 绑定按钮命令 - folder_button.config(command=process_folder_wrapper) - file_button.config(command=process_file_wrapper) - - return root - - -if __name__ == "__main__": - # 请确保以下函数已经正确导入或定义: - # process_floder, process_file, find_csv_files, savecsv - - app = create_gui() - app.mainloop() diff --git a/src/ui/__init__.py b/src/ui/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/utils/__init__.py b/src/utils/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/tools/HumanDataCheck.py b/tools/HumanDataCheck.py similarity index 58% rename from src/tools/HumanDataCheck.py rename to tools/HumanDataCheck.py index c593057..692187e 100644 --- a/src/tools/HumanDataCheck.py +++ b/tools/HumanDataCheck.py @@ -3,9 +3,6 @@ import csv import os -from src.core.paths import PROJECT_ROOT, image_path, data_path - - class ArknightsApp: def __init__(self, root): self.root = root @@ -17,15 +14,9 @@ def __init__(self, root): self.root.configure(bg=self.BG_COLOR) # 绑定快捷键 - self.root.bind( - "", lambda event: self.show_prev_row() - ) # 小键盘左键 - self.root.bind( - "", lambda event: self.show_next_row() - ) # 小键盘右键 - self.root.bind( - "", lambda event: self.delete_current_row() - ) # 删除键 + self.root.bind("", lambda event: self.show_prev_row()) # 小键盘左键 + self.root.bind("", lambda event: self.show_next_row()) # 小键盘右键 + self.root.bind("", lambda event: self.delete_current_row()) # 删除键 # 创建顶部和底部框架 self.top_frame = tk.Frame(root, bg=self.BG_COLOR) @@ -34,49 +25,28 @@ def __init__(self, root): self.bottom_frame.pack(pady=10) # 创建按钮 - btn_style = { - "bg": "#555555", - "fg": self.FG_COLOR, - "activebackground": "#777777", - "activeforeground": self.FG_COLOR, - } - self.next_button = tk.Button( - root, text="下一个", command=self.show_next_row, **btn_style - ) + btn_style = {"bg": "#555555", "fg": self.FG_COLOR, "activebackground": "#777777", "activeforeground": self.FG_COLOR} + self.next_button = tk.Button(root, text="下一个", command=self.show_next_row, **btn_style) self.next_button.pack(side=tk.RIGHT, padx=10, pady=10) - self.prev_button = tk.Button( - root, text="上一个", command=self.show_prev_row, **btn_style - ) + self.prev_button = tk.Button(root, text="上一个", command=self.show_prev_row, **btn_style) self.prev_button.pack(side=tk.RIGHT, padx=10, pady=10) - self.delete_button = tk.Button( - root, text="删除数据", command=self.delete_current_row, **btn_style - ) + self.delete_button = tk.Button(root, text="删除数据", command=self.delete_current_row, **btn_style) self.delete_button.pack(side=tk.RIGHT, padx=10, pady=10) # 添加行号显示和跳转功能 - self.row_label = tk.Label( - root, text="当前行号: 0", bg=self.BG_COLOR, fg=self.FG_COLOR - ) + self.row_label = tk.Label(root, text="当前行号: 0", bg=self.BG_COLOR, fg=self.FG_COLOR) self.row_label.pack(side=tk.LEFT, padx=10) - self.row_entry = tk.Entry( - root, - width=5, - bg="#555555", - fg=self.FG_COLOR, - insertbackground=self.FG_COLOR, - ) + self.row_entry = tk.Entry(root, width=5, bg="#555555", fg=self.FG_COLOR, insertbackground=self.FG_COLOR) self.row_entry.pack(side=tk.LEFT, padx=5) - self.jump_button = tk.Button( - root, text="跳转", command=self.jump_to_row, **btn_style - ) + self.jump_button = tk.Button(root, text="跳转", command=self.jump_to_row, **btn_style) self.jump_button.pack(side=tk.LEFT, padx=5) # 初始化数据 - self.data = self.read_csv(PROJECT_ROOT / "data" / "raw" / "arknights.csv") + self.data = self.read_csv("arknights.csv") self.current_row_index = 0 # 加载图片 @@ -91,7 +61,7 @@ def read_csv(self, file_path): with open(file_path, "r", encoding="utf-8") as csvfile: reader = csv.reader(csvfile) header = next(reader) - self.MONSTER_COUNT = sum(1 for col in header if col.endswith("L")) + self.MONSTER_COUNT = sum(1 for col in header if col.endswith('L')) for row in reader: data.append(row) return data @@ -99,12 +69,10 @@ def read_csv(self, file_path): def load_all_images(self): """加载所有图片""" images = {} - base_dir = os.path.dirname( - os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - ) - monster_csv_path = data_path("monster_greenvine.csv") - images_dir = data_path("images") - + base_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + monster_csv_path = os.path.join(base_dir, "monster_greenvine.csv") + images_dir = os.path.join(base_dir, "images") + id_to_name = {} try: with open(monster_csv_path, "r", encoding="utf-8-sig") as f: @@ -126,9 +94,7 @@ def load_all_images(self): else: # 尝试用“名称”列备用 try: - with open( - monster_csv_path, "r", encoding="utf-8-sig" - ) as f: + with open(monster_csv_path, "r", encoding="utf-8-sig") as f: reader = csv.reader(f) header = next(reader) id_idx = header.index("id") @@ -136,13 +102,9 @@ def load_all_images(self): for row in reader: if int(row[id_idx]) == i: alt_name = row[alt_name_idx] - alt_path = os.path.join( - images_dir, f"{alt_name}.png" - ) + alt_path = os.path.join(images_dir, f"{alt_name}.png") if os.path.exists(alt_path): - image = Image.open(alt_path).resize( - (80, 80) - ) + image = Image.open(alt_path).resize((80, 80)) images[str(i)] = ImageTk.PhotoImage(image) break except Exception: @@ -170,36 +132,16 @@ def show_row(self, row_index): value = float(value) if value > 0: # 仅显示非0值 if self.images.get(str(i)): - tk.Label( - self.top_frame, - image=self.images[str(i)], - bg=self.BG_COLOR, - ).grid(row=0, column=i - 1, padx=2) + tk.Label(self.top_frame, image=self.images[str(i)], bg=self.BG_COLOR).grid(row=0, column=i - 1, padx=2) else: - tk.Label( - self.top_frame, - text=f"ID:{i}", - font=("Arial", 12), - bg=self.BG_COLOR, - fg=self.FG_COLOR, - ).grid(row=0, column=i - 1, padx=2) - tk.Label( - self.top_frame, - text=str(int(value)), - font=("Arial", 16, "bold"), - bg=self.BG_COLOR, - fg=self.FG_COLOR, - ).grid(row=1, column=i - 1) + tk.Label(self.top_frame, text=f"ID:{i}", font=("Arial", 12), bg=self.BG_COLOR, fg=self.FG_COLOR).grid(row=0, column=i - 1, padx=2) + tk.Label(self.top_frame, text=str(int(value)), font=("Arial", 16, "bold"), bg=self.BG_COLOR, fg=self.FG_COLOR).grid(row=1, column=i - 1) except ValueError: - print( - f"Skipping invalid value: {row[i - 1]} at column {i - 1}" - ) + print(f"Skipping invalid value: {row[i - 1]} at column {i - 1}") # 插入空白间隔 gap_column = self.MONSTER_COUNT - 1 # 间隔列索引 - tk.Label(self.top_frame, text="", bg=self.BG_COLOR).grid( - row=0, column=gap_column, padx=50 - ) # 添加水平间距 + tk.Label(self.top_frame, text="", bg=self.BG_COLOR).grid(row=0, column=gap_column, padx=50) # 添加水平间距 # 显示右方怪物 for i in range(self.MONSTER_COUNT + 1, self.MONSTER_COUNT * 2 + 1): @@ -209,39 +151,21 @@ def show_row(self, row_index): if value > 0: # 仅显示非0值 img_idx = str(i - self.MONSTER_COUNT) if self.images.get(img_idx): - tk.Label( - self.top_frame, - image=self.images[img_idx], - bg=self.BG_COLOR, - ).grid(row=0, column=i - 1, padx=2) + tk.Label(self.top_frame, image=self.images[img_idx], bg=self.BG_COLOR).grid(row=0, column=i - 1, padx=2) else: - tk.Label( - self.top_frame, - text=f"ID:{img_idx}", - font=("Arial", 12), - bg=self.BG_COLOR, - fg=self.FG_COLOR, - ).grid(row=0, column=i - 1, padx=2) - tk.Label( - self.top_frame, - text=str(int(value)), - font=("Arial", 16, "bold"), - bg=self.BG_COLOR, - fg=self.FG_COLOR, - ).grid(row=1, column=i - 1) + tk.Label(self.top_frame, text=f"ID:{img_idx}", font=("Arial", 12), bg=self.BG_COLOR, fg=self.FG_COLOR).grid(row=0, column=i - 1, padx=2) + tk.Label(self.top_frame, text=str(int(value)), font=("Arial", 16, "bold"), bg=self.BG_COLOR, fg=self.FG_COLOR).grid(row=1, column=i - 1) except ValueError: - print( - f"Skipping invalid value: {row[i - 1]} at column {i - 1}" - ) + print(f"Skipping invalid value: {row[i - 1]} at column {i - 1}") # 清空底部框架内容 for widget in self.bottom_frame.winfo_children(): - widget.destroy() + widget.destroy() # 获取图片路径 base_dir = os.path.dirname(os.path.abspath(__file__)) img_name = row[-1] - + orig_path = os.path.join(base_dir, "images", img_name + ".jpg") res_path = os.path.join(base_dir, "images", img_name + "_result.jpg") @@ -251,11 +175,9 @@ def show_row(self, row_index): orig_path = os.path.join(base_dir, img_name + ".jpg") if not os.path.exists(orig_path): orig_path = None - + if not os.path.exists(res_path): - res_path = os.path.join( - base_dir, "images", img_name + "_result.png" - ) + res_path = os.path.join(base_dir, "images", img_name + "_result.png") if not os.path.exists(res_path): res_path = None @@ -270,78 +192,37 @@ def show_row(self, row_index): top = h * 0.8 right = w * 0.8 bottom = h - cropped_image = orig_image.crop( - (int(left), int(top), int(right), int(bottom)) - ) - + cropped_image = orig_image.crop((int(left), int(top), int(right), int(bottom))) + # 如果需要限制大小,可解除下面注释 # max_size = (800, 200) # cropped_image.thumbnail(max_size, Image.Resampling.LANCZOS) - + cropped_image_tk = ImageTk.PhotoImage(cropped_image) - tk.Label( - self.bottom_frame, image=cropped_image_tk, bg=self.BG_COLOR - ).pack(side=tk.TOP, pady=5) + tk.Label(self.bottom_frame, image=cropped_image_tk, bg=self.BG_COLOR).pack(side=tk.TOP, pady=5) self.bottom_frame.images.append(cropped_image_tk) else: - tk.Label( - self.bottom_frame, - text=f"找不到原图: {img_name}", - bg=self.BG_COLOR, - fg=self.FG_COLOR, - ).pack(side=tk.TOP, pady=5) + tk.Label(self.bottom_frame, text=f"找不到原图: {img_name}", bg=self.BG_COLOR, fg=self.FG_COLOR).pack(side=tk.TOP, pady=5) # 2. 显示 L/R 文字和色条(中间) result_text = row[self.MONSTER_COUNT * 2].strip() - tk.Label( - self.bottom_frame, - text=result_text, - font=("Arial", 20, "bold"), - bg=self.BG_COLOR, - fg=self.FG_COLOR, - ).pack(side=tk.TOP, pady=2) - + tk.Label(self.bottom_frame, text=result_text, font=("Arial", 20, "bold"), bg=self.BG_COLOR, fg=self.FG_COLOR).pack(side=tk.TOP, pady=2) + # 添加色彩长条 Canvas bar_width = 200 bar_height = 15 - color_bar = tk.Canvas( - self.bottom_frame, - width=bar_width, - height=bar_height, - highlightthickness=0, - bg=self.BG_COLOR, - ) + color_bar = tk.Canvas(self.bottom_frame, width=bar_width, height=bar_height, highlightthickness=0, bg=self.BG_COLOR) color_bar.pack(side=tk.TOP, pady=5) - - if result_text.upper() == "L": - color_bar.create_rectangle( - 0, 0, bar_width // 2, bar_height, fill="yellow", outline="" - ) - color_bar.create_rectangle( - bar_width // 2, - 0, - bar_width, - bar_height, - fill="gray", - outline="", - ) - elif result_text.upper() == "R": - color_bar.create_rectangle( - 0, 0, bar_width // 2, bar_height, fill="gray", outline="" - ) - color_bar.create_rectangle( - bar_width // 2, - 0, - bar_width, - bar_height, - fill="yellow", - outline="", - ) + + if result_text.upper() == 'L': + color_bar.create_rectangle(0, 0, bar_width // 2, bar_height, fill="yellow", outline="") + color_bar.create_rectangle(bar_width // 2, 0, bar_width, bar_height, fill="gray", outline="") + elif result_text.upper() == 'R': + color_bar.create_rectangle(0, 0, bar_width // 2, bar_height, fill="gray", outline="") + color_bar.create_rectangle(bar_width // 2, 0, bar_width, bar_height, fill="yellow", outline="") else: # 默认颜色(异常数据时) - color_bar.create_rectangle( - 0, 0, bar_width, bar_height, fill="gray", outline="" - ) + color_bar.create_rectangle(0, 0, bar_width, bar_height, fill="gray", outline="") # 3. 显示结果图(下方) if res_path: @@ -349,17 +230,10 @@ def show_row(self, row_index): max_size = (800, 400) res_image.thumbnail(max_size, Image.Resampling.LANCZOS) res_image_tk = ImageTk.PhotoImage(res_image) - tk.Label( - self.bottom_frame, image=res_image_tk, bg=self.BG_COLOR - ).pack(side=tk.TOP, pady=5) + tk.Label(self.bottom_frame, image=res_image_tk, bg=self.BG_COLOR).pack(side=tk.TOP, pady=5) self.bottom_frame.images.append(res_image_tk) else: - tk.Label( - self.bottom_frame, - text=f"找不到结果图", - bg=self.BG_COLOR, - fg=self.FG_COLOR, - ).pack(side=tk.TOP, pady=5) + tk.Label(self.bottom_frame, text=f"找不到结果图", bg=self.BG_COLOR, fg=self.FG_COLOR).pack(side=tk.TOP, pady=5) # 更新行号显示 self.row_label.config(text=f"当前行号: {row_index + 1}") @@ -398,7 +272,7 @@ def delete_current_row(self): # 获取当前行最后一列的图片路径 image_name = self.data[self.current_row_index][-1] base_dir = os.path.dirname(os.path.abspath(__file__)) - + # 删除相关的图片文件(如果存在) possible_exts = [".jpg", "_result.jpg", ".png", ""] for ext in possible_exts: @@ -409,14 +283,12 @@ def delete_current_row(self): del self.data[self.current_row_index] # 从内存中删除当前行 # 将修改后的数据写回 CSV 文件 - with open( - "arknights.csv", "w", newline="", encoding="utf-8" - ) as csvfile: + with open("arknights.csv", "w", newline="", encoding="utf-8") as csvfile: writer = csv.writer(csvfile) writer.writerow( - [f"{i}L" for i in range(1, self.MONSTER_COUNT + 1)] - + [f"{i}R" for i in range(1, self.MONSTER_COUNT + 1)] - + ["Result", "ImgPath"] + [f"{i}L" for i in range(1, self.MONSTER_COUNT + 1)] + + [f"{i}R" for i in range(1, self.MONSTER_COUNT + 1)] + + ["Result", "ImgPath"] ) writer.writerows(self.data) # 更新显示 @@ -429,4 +301,4 @@ def delete_current_row(self): if __name__ == "__main__": root = tk.Tk() app = ArknightsApp(root) - root.mainloop() + root.mainloop() \ No newline at end of file diff --git a/src/tools/battlefield_composite/.gitignore b/tools/battlefield_composite/.gitignore similarity index 100% rename from src/tools/battlefield_composite/.gitignore rename to tools/battlefield_composite/.gitignore diff --git a/src/tools/battlefield_composite/battlefield_composite.py b/tools/battlefield_composite/battlefield_composite.py similarity index 87% rename from src/tools/battlefield_composite/battlefield_composite.py rename to tools/battlefield_composite/battlefield_composite.py index becd82c..58dd16e 100644 --- a/src/tools/battlefield_composite/battlefield_composite.py +++ b/tools/battlefield_composite/battlefield_composite.py @@ -67,9 +67,7 @@ def get_random_png_frame(png_folder): # 随机选择一个png文件 random_png = random.choice(png_list) print(f"随机选择的PNG文件: {random_png}") - frame = cv2.imread( - str(random_png), cv2.IMREAD_UNCHANGED - ) # Read with alpha channel + frame = cv2.imread(str(random_png), cv2.IMREAD_UNCHANGED) # Read with alpha channel if frame is None: raise Exception(f"无法读取PNG文件: {random_png}") return frame @@ -125,10 +123,7 @@ def compose_frame(frame, background, x, y): # 进行alpha混合 for c in range(0, 3): - roi[:, :, c] = ( - roi[:, :, c] * (1 - alpha_channel) - + overlay_colors[:, :, c] * alpha_channel - ) + roi[:, :, c] = roi[:, :, c] * (1 - alpha_channel) + overlay_colors[:, :, c] * alpha_channel # 将结果放回原图 background[y_start:y_end, x_start:x_end] = roi @@ -138,9 +133,7 @@ def compose_frame(frame, background, x, y): def composite_random_frame(): # 读取战场背景图 - battlefield = cv2.imread( - "./tools/battlefield_composite/monster_images/IM-1.png" - ) + battlefield = cv2.imread("./tools/battlefield_composite/monster_images/IM-1.png") battlefield = cv2.resize(battlefield, (1920, 1080)) if battlefield is None: raise Exception("无法读取战场背景图") @@ -163,9 +156,7 @@ def composite_random_frame(): for i in range(10): x, y = frame_list[i] - frame = get_random_png_frame( - "./tools/battlefield_composite/monster_images/Arc_Frontliner_Leader-Move" - ) + frame = get_random_png_frame("./tools/battlefield_composite/monster_images/Arc_Frontliner_Leader-Move") # 缩放 factor = (y / bg_height) * 0.4 + 0.6 @@ -182,16 +173,9 @@ def composite_random_frame(): ellipse_x = int(x + center[0]) ellipse_y = int(y + center[1] + new_frame_height * 0.30) black = (0, 0, 0) - cv2.ellipse( - battlefield, - (ellipse_x, ellipse_y), - (int(new_frame_width * 0.100), int(new_frame_height * 0.025)), - 0, - 0, - 360, - black, - -1, - ) + cv2.ellipse(battlefield, (ellipse_x, ellipse_y), + (int(new_frame_width * 0.100), int(new_frame_height * 0.025)), + 0, 0, 360, black, -1) # 画框 cv2.rectangle(battlefield, (x, y), (x + x1, y + y1), (0, 255, 0), 2) @@ -212,10 +196,9 @@ def composite_random_frame(): # 将渐变掩码应用到图像上 battlefield = compose_frame(gradient_mask, battlefield, 0, 0) + # 保存结果 - cv2.imwrite( - "./tools/battlefield_composite/battlefield_composite.png", battlefield - ) + cv2.imwrite("./tools/battlefield_composite/battlefield_composite.png", battlefield) def crop_to_bounding_box(image): @@ -231,9 +214,7 @@ def crop_to_bounding_box(image): # 检查图像是否包含alpha通道 if image.shape[2] < 4: # 如果没有alpha通道,则无法进行基于透明度的裁切 - print( - "警告: 图像不包含alpha通道,无法进行基于透明度的裁切。返回原图。" - ) + print("警告: 图像不包含alpha通道,无法进行基于透明度的裁切。返回原图。") return image # 获取alpha通道 @@ -253,7 +234,7 @@ def crop_to_bounding_box(image): x, y, w, h = cv2.boundingRect(coords) # 裁切图像 - cropped_image = image[y : y + h, x : x + w] + cropped_image = image[y:y+h, x:x+w] center_x = image.shape[1] / 2 - x center_y = image.shape[0] / 2 - y center = (center_x, center_y) diff --git a/tools/battlefield_composite/extract_webm_frames.py b/tools/battlefield_composite/extract_webm_frames.py new file mode 100644 index 0000000..ca85e6a --- /dev/null +++ b/tools/battlefield_composite/extract_webm_frames.py @@ -0,0 +1,92 @@ +import logging +from pathlib import Path +import subprocess +import os + + +monster_name = { + "弧光锋卫长" : "Arc_Frontliner_Leader", + "炮击组长" : "Mortar_Gunner_Leader", + "复仇者" : "Hateful_Avenger", + "重装防御者" : "Heavy_Defender", + "“庞贝”" : "Pompeii", + "冰原术师" : "Icefield_Caster", + "沸血骑士团精锐" : "Bloodboil_Knightclub_Elite", + "高塔术师" : "Spire_Caster", + "固海凿石者" : "Ocean_Stonecutter", + # "呼啸骑士团学徒" : "Roar_Knightclub_Trainee", + "湖畔志愿者" : "Lakeside_Volunteer", + "杰斯顿·威廉姆斯" : "Jesselton_Williams", + "酸液源石虫·α" : "Acid_Originium_Slug_α", + "神射手囚犯" : "Elite_Sniper_Prisoner", + "拳手囚犯" : "Pugilist_Prisoner", + "染污躯壳" : "Tainted_Carcass", + "泥岩巨像" : "Mudrock_Colossus", + "狂暴的猎狗pro" : "Rabid_Hound_Pro", + "宿主拾荒者" : "Possessed_Veteran_Junkman", + "狂暴宿主组长" : "Enraged_Possessed_Leader", + "萨卡兹大剑手" : "Sarkaz_Greatswordsman", + "矿脉守卫" : "Vein_Guardian", + "山海众窥魅人" : "Shanhaizhong_Seer", + "提亚卡乌好战者" : "Tiacauh_Fanatic", + "码头水手" : "Dockworker", + "变异巨岩蛛" : "Mutant_Giant_Rock_Spider", + "萨卡兹子裔链术师" : "Sarkaz_Heirbearer_Chain_Caster", + "温顺的武装驮兽" : "Armored_Burdenbeast", + "木裂战士" : "Shattered_Champion", + "深溟裂礁者" : "Nethersea_Reefbreaker", + "山雪鬼" : "Tschäggättä", + "高普尼克" : "Gopnik", + "冰爆源石虫" : "Infused_Glacial_Originium_Slug", + "反装甲步兵" : "Anti-Armor_Infantry", + "“钳钳生风”" : "Consortium_of_Pincers", + "富营养的穿刺者" : "Nourished_Piercer", + "高级武装人员" : "Senior_Armed_Militant", + "朗姆酒推荐者" : "Rum_Connoisseur", + "烈酒级醒酒助手" : "Whiskey-Grade_Waker-Upper", + "萨卡兹王庭军术师" : "Sarkaz_Royal_Court_Caster", + # "灰尾香主" : "Greytail_Leader", + "阵地击人手" : "Field_Bludgeoner", + "源石畸变体" : "Originiutant", + "提亚卡乌破坏王" : "Tiacauh_Annihilator", + "逐腐兽" : "Rotchaser", + "高能源石虫" : "Infused_Originium_Slug", + "风情街“星术师”" : "Fashion_Street_Stellar_Caster", + "田鼷力士" : "Fieldmus_Bruiser", + "残党萨克斯手" : "Remnant_Saxophonist", + "散华骑士团学徒" : "Nova_Knightclub_Trainee", + "“阿咬”" : "Bitey", + # "“门”" : "", + "“投石机”" : "Catapult", + # "“复仇者”" : '"Hateful Avenger"', + # "扎罗,“狼之主”" : "Zaaro", + # "“自在”" : "Free", + # "灼热源石虫" : "Blazing_Originium_Slug", + # "萨卡兹子裔责罚者" : "Sarkaz_Heirbearer_Punisher", + # "" : "", +} + +def extract_webm(webm_path: Path, output_folder: Path): + extract_webm_cmd = f"ffmpeg -c:v libvpx -i \"{webm_path}\" \"{output_folder}/frame%d.png\"" + subprocess.run(extract_webm_cmd, shell=True, check=True) + + +def main(): + webm_folder = "./tools/battlefield_composite/monster_images" + for webm_path in Path(webm_folder).glob("*.webm"): + name_split = webm_path.parts[-1].split("-") + print(name_split) + if (name := monster_name.get(name_split[0])) is not None: + output_directory = webm_path.parent / (name + "-" + name_split[3]) + print(output_directory) + if not output_directory.exists(): + output_directory.mkdir(exist_ok=True) + extract_webm(webm_path, output_directory) + else: + logging.warning(f"{output_directory} already exists") + else: + logging.error(f"Name: {name_split[0]} not found!") + + +if __name__ == "__main__": + main() diff --git a/src/tools/battlefield_composite/monster_images/IM-1.png b/tools/battlefield_composite/monster_images/IM-1.png similarity index 100% rename from src/tools/battlefield_composite/monster_images/IM-1.png rename to tools/battlefield_composite/monster_images/IM-1.png diff --git a/src/tools/battlefield_composite/monster_images/battlefield_empty.jpg b/tools/battlefield_composite/monster_images/battlefield_empty.jpg similarity index 100% rename from src/tools/battlefield_composite/monster_images/battlefield_empty.jpg rename to tools/battlefield_composite/monster_images/battlefield_empty.jpg diff --git "a/src/tools/battlefield_composite/monster_images/\342\200\234\345\244\215\344\273\207\350\200\205\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\342\200\234\345\244\215\344\273\207\350\200\205\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\342\200\234\345\244\215\344\273\207\350\200\205\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\342\200\234\345\244\215\344\273\207\350\200\205\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\342\200\234\345\272\236\350\264\235\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\342\200\234\345\272\236\350\264\235\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\342\200\234\345\272\236\350\264\235\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\342\200\234\345\272\236\350\264\235\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\342\200\234\346\212\225\347\237\263\346\234\272\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-A_Move-x1.webm" "b/tools/battlefield_composite/monster_images/\342\200\234\346\212\225\347\237\263\346\234\272\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-A_Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\342\200\234\346\212\225\347\237\263\346\234\272\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-A_Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\342\200\234\346\212\225\347\237\263\346\234\272\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-A_Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\342\200\234\350\207\252\345\234\250\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\342\200\234\350\207\252\345\234\250\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\342\200\234\350\207\252\345\234\250\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\342\200\234\350\207\252\345\234\250\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\342\200\234\351\222\263\351\222\263\347\224\237\351\243\216\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\342\200\234\351\222\263\351\222\263\347\224\237\351\243\216\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\342\200\234\351\222\263\351\222\263\347\224\237\351\243\216\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\342\200\234\351\222\263\351\222\263\347\224\237\351\243\216\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\342\200\234\351\227\250\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\342\200\234\351\227\250\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\342\200\234\351\227\250\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\342\200\234\351\227\250\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\342\200\234\351\230\277\345\222\254\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\342\200\234\351\230\277\345\222\254\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\342\200\234\351\230\277\345\222\254\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\342\200\234\351\230\277\345\222\254\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\345\206\260\345\216\237\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\345\206\260\345\216\237\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\345\206\260\345\216\237\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\345\206\260\345\216\237\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\345\206\260\347\210\206\346\272\220\347\237\263\350\231\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\345\206\260\347\210\206\346\272\220\347\237\263\350\231\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\345\206\260\347\210\206\346\272\220\347\237\263\350\231\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\345\206\260\347\210\206\346\272\220\347\237\263\350\231\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\345\217\215\350\243\205\347\224\262\346\255\245\345\205\265-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_A-x1.webm" "b/tools/battlefield_composite/monster_images/\345\217\215\350\243\205\347\224\262\346\255\245\345\205\265-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_A-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\345\217\215\350\243\205\347\224\262\346\255\245\345\205\265-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_A-x1.webm" rename to "tools/battlefield_composite/monster_images/\345\217\215\350\243\205\347\224\262\346\255\245\345\205\265-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_A-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\345\217\230\345\274\202\345\267\250\345\262\251\350\233\233-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\345\217\230\345\274\202\345\267\250\345\262\251\350\233\233-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\345\217\230\345\274\202\345\267\250\345\262\251\350\233\233-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\345\217\230\345\274\202\345\267\250\345\262\251\350\233\233-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\345\221\274\345\225\270\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\345\221\274\345\225\270\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\345\221\274\345\225\270\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\345\221\274\345\225\270\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\345\233\272\346\265\267\345\207\277\347\237\263\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\345\233\272\346\265\267\345\207\277\347\237\263\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\345\233\272\346\265\267\345\207\277\347\237\263\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\345\233\272\346\265\267\345\207\277\347\237\263\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\345\244\215\344\273\207\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\345\244\215\344\273\207\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\345\244\215\344\273\207\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\345\244\215\344\273\207\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\345\256\277\344\270\273\346\213\276\350\215\222\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\345\256\277\344\270\273\346\213\276\350\215\222\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\345\256\277\344\270\273\346\213\276\350\215\222\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\345\256\277\344\270\273\346\213\276\350\215\222\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\345\257\214\350\220\245\345\205\273\347\232\204\347\251\277\345\210\272\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\345\257\214\350\220\245\345\205\273\347\232\204\347\251\277\345\210\272\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\345\257\214\350\220\245\345\205\273\347\232\204\347\251\277\345\210\272\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\345\257\214\350\220\245\345\205\273\347\232\204\347\251\277\345\210\272\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\345\261\261\346\265\267\344\274\227\347\252\245\351\255\205\344\272\272-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\345\261\261\346\265\267\344\274\227\347\252\245\351\255\205\344\272\272-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\345\261\261\346\265\267\344\274\227\347\252\245\351\255\205\344\272\272-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\345\261\261\346\265\267\344\274\227\347\252\245\351\255\205\344\272\272-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\345\261\261\351\233\252\351\254\274-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\345\261\261\351\233\252\351\254\274-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\345\261\261\351\233\252\351\254\274-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\345\261\261\351\233\252\351\254\274-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\345\274\247\345\205\211\351\224\213\345\215\253\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\345\274\247\345\205\211\351\224\213\345\215\253\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\345\274\247\345\205\211\351\224\213\345\215\253\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\345\274\247\345\205\211\351\224\213\345\215\253\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\346\211\216\347\275\227\357\274\214\342\200\234\347\213\274\344\271\213\344\270\273\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-A_Move-x1.webm" "b/tools/battlefield_composite/monster_images/\346\211\216\347\275\227\357\274\214\342\200\234\347\213\274\344\271\213\344\270\273\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-A_Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\346\211\216\347\275\227\357\274\214\342\200\234\347\213\274\344\271\213\344\270\273\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-A_Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\346\211\216\347\275\227\357\274\214\342\200\234\347\213\274\344\271\213\344\270\273\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-A_Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\346\213\263\346\211\213\345\233\232\347\212\257-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_red-x1.webm" "b/tools/battlefield_composite/monster_images/\346\213\263\346\211\213\345\233\232\347\212\257-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_red-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\346\213\263\346\211\213\345\233\232\347\212\257-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_red-x1.webm" rename to "tools/battlefield_composite/monster_images/\346\213\263\346\211\213\345\233\232\347\212\257-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_red-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\346\217\220\344\272\232\345\215\241\344\271\214\345\245\275\346\210\230\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\346\217\220\344\272\232\345\215\241\344\271\214\345\245\275\346\210\230\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\346\217\220\344\272\232\345\215\241\344\271\214\345\245\275\346\210\230\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\346\217\220\344\272\232\345\215\241\344\271\214\345\245\275\346\210\230\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\346\217\220\344\272\232\345\215\241\344\271\214\347\240\264\345\235\217\347\216\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\346\217\220\344\272\232\345\215\241\344\271\214\347\240\264\345\235\217\347\216\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\346\217\220\344\272\232\345\215\241\344\271\214\347\240\264\345\235\217\347\216\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\346\217\220\344\272\232\345\215\241\344\271\214\347\240\264\345\235\217\347\216\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\346\225\243\345\215\216\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\346\225\243\345\215\216\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\346\225\243\345\215\216\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\346\225\243\345\215\216\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\346\234\227\345\247\206\351\205\222\346\216\250\350\215\220\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\346\234\227\345\247\206\351\205\222\346\216\250\350\215\220\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\346\234\227\345\247\206\351\205\222\346\216\250\350\215\220\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\346\234\227\345\247\206\351\205\222\346\216\250\350\215\220\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\346\234\250\350\243\202\346\210\230\345\243\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\346\234\250\350\243\202\346\210\230\345\243\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\346\234\250\350\243\202\346\210\230\345\243\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\346\234\250\350\243\202\346\210\230\345\243\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\346\235\260\346\226\257\351\241\277\302\267\345\250\201\345\273\211\345\247\206\346\226\257-\351\273\230\350\256\244-\346\210\230\346\226\227-C1_Move-x1.webm" "b/tools/battlefield_composite/monster_images/\346\235\260\346\226\257\351\241\277\302\267\345\250\201\345\273\211\345\247\206\346\226\257-\351\273\230\350\256\244-\346\210\230\346\226\227-C1_Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\346\235\260\346\226\257\351\241\277\302\267\345\250\201\345\273\211\345\247\206\346\226\257-\351\273\230\350\256\244-\346\210\230\346\226\227-C1_Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\346\235\260\346\226\257\351\241\277\302\267\345\250\201\345\273\211\345\247\206\346\226\257-\351\273\230\350\256\244-\346\210\230\346\226\227-C1_Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\346\237\223\346\261\241\350\272\257\345\243\263-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\346\237\223\346\261\241\350\272\257\345\243\263-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\346\237\223\346\261\241\350\272\257\345\243\263-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\346\237\223\346\261\241\350\272\257\345\243\263-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\346\256\213\345\205\232\350\220\250\345\205\213\346\226\257\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\346\256\213\345\205\232\350\220\250\345\205\213\346\226\257\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\346\256\213\345\205\232\350\220\250\345\205\213\346\226\257\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\346\256\213\345\205\232\350\220\250\345\205\213\346\226\257\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\346\262\270\350\241\200\351\252\221\345\243\253\345\233\242\347\262\276\351\224\220-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\346\262\270\350\241\200\351\252\221\345\243\253\345\233\242\347\262\276\351\224\220-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\346\262\270\350\241\200\351\252\221\345\243\253\345\233\242\347\262\276\351\224\220-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\346\262\270\350\241\200\351\252\221\345\243\253\345\233\242\347\262\276\351\224\220-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\346\263\245\345\262\251\345\267\250\345\203\217-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\346\263\245\345\262\251\345\267\250\345\203\217-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\346\263\245\345\262\251\345\267\250\345\203\217-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\346\263\245\345\262\251\345\267\250\345\203\217-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\346\267\261\346\272\237\350\243\202\347\244\201\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\346\267\261\346\272\237\350\243\202\347\244\201\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\346\267\261\346\272\237\350\243\202\347\244\201\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\346\267\261\346\272\237\350\243\202\347\244\201\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\346\270\251\351\241\272\347\232\204\346\255\246\350\243\205\351\251\256\345\205\275-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\346\270\251\351\241\272\347\232\204\346\255\246\350\243\205\351\251\256\345\205\275-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\346\270\251\351\241\272\347\232\204\346\255\246\350\243\205\351\251\256\345\205\275-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\346\270\251\351\241\272\347\232\204\346\255\246\350\243\205\351\251\256\345\205\275-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\346\271\226\347\225\224\345\277\227\346\204\277\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\346\271\226\347\225\224\345\277\227\346\204\277\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\346\271\226\347\225\224\345\277\227\346\204\277\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\346\271\226\347\225\224\345\277\227\346\204\277\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\346\272\220\347\237\263\347\225\270\345\217\230\344\275\223-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\346\272\220\347\237\263\347\225\270\345\217\230\344\275\223-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\346\272\220\347\237\263\347\225\270\345\217\230\344\275\223-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\346\272\220\347\237\263\347\225\270\345\217\230\344\275\223-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\347\201\260\345\260\276\351\246\231\344\270\273-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\347\201\260\345\260\276\351\246\231\344\270\273-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\347\201\260\345\260\276\351\246\231\344\270\273-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\347\201\260\345\260\276\351\246\231\344\270\273-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\347\202\256\345\207\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Attack-x1.webm" "b/tools/battlefield_composite/monster_images/\347\202\256\345\207\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Attack-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\347\202\256\345\207\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Attack-x1.webm" rename to "tools/battlefield_composite/monster_images/\347\202\256\345\207\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Attack-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\347\202\256\345\207\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\347\202\256\345\207\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\347\202\256\345\207\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\347\202\256\345\207\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\347\203\210\351\205\222\347\272\247\351\206\222\351\205\222\345\212\251\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\347\203\210\351\205\222\347\272\247\351\206\222\351\205\222\345\212\251\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\347\203\210\351\205\222\347\272\247\351\206\222\351\205\222\345\212\251\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\347\203\210\351\205\222\347\272\247\351\206\222\351\205\222\345\212\251\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\347\213\202\346\232\264\345\256\277\344\270\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\347\213\202\346\232\264\345\256\277\344\270\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\347\213\202\346\232\264\345\256\277\344\270\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\347\213\202\346\232\264\345\256\277\344\270\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\347\213\202\346\232\264\347\232\204\347\214\216\347\213\227pro-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" "b/tools/battlefield_composite/monster_images/\347\213\202\346\232\264\347\232\204\347\214\216\347\213\227pro-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\347\213\202\346\232\264\347\232\204\347\214\216\347\213\227pro-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" rename to "tools/battlefield_composite/monster_images/\347\213\202\346\232\264\347\232\204\347\214\216\347\213\227pro-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\347\224\260\351\274\267\345\212\233\345\243\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\347\224\260\351\274\267\345\212\233\345\243\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\347\224\260\351\274\267\345\212\233\345\243\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\347\224\260\351\274\267\345\212\233\345\243\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\347\237\277\350\204\211\345\256\210\345\215\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\347\237\277\350\204\211\345\256\210\345\215\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\347\237\277\350\204\211\345\256\210\345\215\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\347\237\277\350\204\211\345\256\210\345\215\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\347\245\236\345\260\204\346\211\213\345\233\232\347\212\257-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\347\245\236\345\260\204\346\211\213\345\233\232\347\212\257-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\347\245\236\345\260\204\346\211\213\345\233\232\347\212\257-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\347\245\236\345\260\204\346\211\213\345\233\232\347\212\257-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\345\244\247\345\211\221\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" "b/tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\345\244\247\345\211\221\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\345\244\247\345\211\221\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" rename to "tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\345\244\247\345\211\221\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\345\255\220\350\243\224\351\223\276\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\345\255\220\350\243\224\351\223\276\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\345\255\220\350\243\224\351\223\276\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\345\255\220\350\243\224\351\223\276\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\347\216\213\345\272\255\345\206\233\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\347\216\213\345\272\255\345\206\233\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\347\216\213\345\272\255\345\206\233\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\347\216\213\345\272\255\345\206\233\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\351\200\220\350\205\220\345\205\275-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\351\200\220\350\205\220\345\205\275-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\351\200\220\350\205\220\345\205\275-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\351\200\220\350\205\220\345\205\275-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\351\205\270\346\266\262\346\272\220\347\237\263\350\231\253\302\267\316\261-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" "b/tools/battlefield_composite/monster_images/\351\205\270\346\266\262\346\272\220\347\237\263\350\231\253\302\267\316\261-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\351\205\270\346\266\262\346\272\220\347\237\263\350\231\253\302\267\316\261-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" rename to "tools/battlefield_composite/monster_images/\351\205\270\346\266\262\346\272\220\347\237\263\350\231\253\302\267\316\261-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\351\207\215\350\243\205\351\230\262\345\276\241\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" "b/tools/battlefield_composite/monster_images/\351\207\215\350\243\205\351\230\262\345\276\241\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\351\207\215\350\243\205\351\230\262\345\276\241\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" rename to "tools/battlefield_composite/monster_images/\351\207\215\350\243\205\351\230\262\345\276\241\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\351\230\265\345\234\260\345\207\273\344\272\272\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\351\230\265\345\234\260\345\207\273\344\272\272\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\351\230\265\345\234\260\345\207\273\344\272\272\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\351\230\265\345\234\260\345\207\273\344\272\272\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\351\243\216\346\203\205\350\241\227\342\200\234\346\230\237\346\234\257\345\270\210\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\351\243\216\346\203\205\350\241\227\342\200\234\346\230\237\346\234\257\345\270\210\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\351\243\216\346\203\205\350\241\227\342\200\234\346\230\237\346\234\257\345\270\210\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\351\243\216\346\203\205\350\241\227\342\200\234\346\230\237\346\234\257\345\270\210\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\351\253\230\345\241\224\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\351\253\230\345\241\224\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\351\253\230\345\241\224\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\351\253\230\345\241\224\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\351\253\230\346\231\256\345\260\274\345\205\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\351\253\230\346\231\256\345\260\274\345\205\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\351\253\230\346\231\256\345\260\274\345\205\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\351\253\230\346\231\256\345\260\274\345\205\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\351\253\230\347\272\247\346\255\246\350\243\205\344\272\272\345\221\230-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\351\253\230\347\272\247\346\255\246\350\243\205\344\272\272\345\221\230-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\351\253\230\347\272\247\346\255\246\350\243\205\344\272\272\345\221\230-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\351\253\230\347\272\247\346\255\246\350\243\205\344\272\272\345\221\230-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git "a/src/tools/battlefield_composite/monster_images/\351\253\230\350\203\275\346\272\220\347\237\263\350\231\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" "b/tools/battlefield_composite/monster_images/\351\253\230\350\203\275\346\272\220\347\237\263\350\231\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" similarity index 100% rename from "src/tools/battlefield_composite/monster_images/\351\253\230\350\203\275\346\272\220\347\237\263\350\231\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" rename to "tools/battlefield_composite/monster_images/\351\253\230\350\203\275\346\272\220\347\237\263\350\231\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" diff --git a/src/tools/battlefield_recognize/class_to_idx.json b/tools/battlefield_recognize/class_to_idx.json similarity index 100% rename from src/tools/battlefield_recognize/class_to_idx.json rename to tools/battlefield_recognize/class_to_idx.json diff --git "a/src/tools/battlefield_recognize/\345\234\272\345\234\260\350\257\206\345\210\253\345\255\227\346\256\265\350\257\264\346\230\216.txt" "b/tools/battlefield_recognize/\345\234\272\345\234\260\350\257\206\345\210\253\345\255\227\346\256\265\350\257\264\346\230\216.txt" similarity index 100% rename from "src/tools/battlefield_recognize/\345\234\272\345\234\260\350\257\206\345\210\253\345\255\227\346\256\265\350\257\264\346\230\216.txt" rename to "tools/battlefield_recognize/\345\234\272\345\234\260\350\257\206\345\210\253\345\255\227\346\256\265\350\257\264\346\230\216.txt" diff --git a/src/tools/convert_model.py b/tools/convert_model.py similarity index 70% rename from src/tools/convert_model.py rename to tools/convert_model.py index db57d57..f55852b 100644 --- a/src/tools/convert_model.py +++ b/tools/convert_model.py @@ -1,22 +1,20 @@ import sys - sys.path.append(".") -from src.models import predict -from src.models import predict_onnx +import predict +from train import UnitAwareTransformer +import predict_onnx import numpy as np -from src.recognition.recognize import MONSTER_COUNT +from recognize import MONSTER_COUNT model_path = "models/best_model_full.pth" - def replace_suffix(s): - idx = s.rfind(".") + idx = s.rfind('.') if idx == -1: - return s + ".onnx" + return s + '.onnx' else: - return s[:idx] + ".onnx" - + return s[:idx] + '.onnx' output_path = replace_suffix(model_path) @@ -25,13 +23,13 @@ def replace_suffix(s): model.export_onnx(output_path) # 导出 ONNX 模型 print(f"模型已成功导出为 ONNX 格式,保存路径: {output_path}") -# 验证导出结果 +#验证导出结果 data_array = np.zeros(MONSTER_COUNT * 2, dtype=np.int64) data_array[28] = 16 data_array[MONSTER_COUNT + 30] = 22 left = data_array[:MONSTER_COUNT] right = data_array[MONSTER_COUNT:] -Onnxmodel = predict_onnx.CannotModel(model_path=output_path) +Onnxmodel = predict_onnx.CannotModel(model_path = output_path) prediction = Onnxmodel.get_prediction(left, right) print("预测结果:", prediction) diff --git a/src/tools/data_cleaning.py b/tools/data_cleaning.py similarity index 97% rename from src/tools/data_cleaning.py rename to tools/data_cleaning.py index dfa5da2..98f86c5 100644 --- a/src/tools/data_cleaning.py +++ b/tools/data_cleaning.py @@ -1,8 +1,6 @@ import pandas as pd import numpy as np -from src.core.paths import PROJECT_ROOT, simulation_path - def clean_data(file_path, output_path): print(f"开始清洗数据文件: {file_path}") @@ -220,6 +218,6 @@ def enhanced_clean(column_data, original_indices, col_idx): if __name__ == "__main__": - input_file = PROJECT_ROOT / "data" / "raw" / "arknights.csv" - output_file = PROJECT_ROOT / "data" / "processed" / "arknights_cleaned.csv" + input_file = "arknights.csv" + output_file = "arknights_cleaned.csv" clean_data(input_file, output_file) diff --git a/src/tools/data_cleaning_with_field_recognize.py b/tools/data_cleaning_with_field_recognize.py similarity index 61% rename from src/tools/data_cleaning_with_field_recognize.py rename to tools/data_cleaning_with_field_recognize.py index 3b0490d..7e1aa72 100644 --- a/src/tools/data_cleaning_with_field_recognize.py +++ b/tools/data_cleaning_with_field_recognize.py @@ -1,8 +1,6 @@ import pandas as pd import numpy as np import os - -from src.core.paths import simulation_path, PROJECT_ROOT import json import re from collections import defaultdict @@ -17,22 +15,22 @@ "altar_vertical": [ {"x": 910, "y": 174, "width": 95, "height": 104}, {"x": 910, "y": 429, "width": 102, "height": 108}, - {"x": 900, "y": 755, "width": 120, "height": 108}, + {"x": 900, "y": 755, "width": 120, "height": 108} ], "block_parallel": [ {"x": 694, "y": 240, "width": 530, "height": 122}, - {"x": 651, "y": 614, "width": 620, "height": 143}, + {"x": 651, "y": 614, "width": 620, "height": 143} ], "block_vertical": [ {"x": 647, "y": 233, "width": 153, "height": 523}, - {"x": 1112, "y": 239, "width": 159, "height": 514}, + {"x": 1112, "y": 239, "width": 159, "height": 514} ], "coil_narrow": [ {"x": 915, "y": 110, "width": 85, "height": 89}, {"x": 815, "y": 257, "width": 86, "height": 98}, {"x": 1024, "y": 258, "width": 79, "height": 98}, {"x": 790, "y": 643, "width": 97, "height": 102}, - {"x": 1031, "y": 639, "width": 102, "height": 108}, + {"x": 1031, "y": 639, "width": 102, "height": 108} ], "coil_wide": [ {"x": 719, "y": 181, "width": 81, "height": 89}, @@ -42,15 +40,21 @@ {"x": 1159, "y": 757, "width": 93, "height": 92}, {"x": 1257, "y": 533, "width": 94, "height": 102}, {"x": 1236, "y": 344, "width": 85, "height": 97}, - {"x": 1120, "y": 180, "width": 75, "height": 91}, + {"x": 1120, "y": 180, "width": 75, "height": 91} + ], + "crossbow_top": [ + {"x": 718, "y": 13, "width": 484, "height": 106} + ], + "fire_side_left": [ + {"x": 98, "y": 246, "width": 184, "height": 281} + ], + "fire_side_right": [ + {"x": 1656, "y": 430, "width": 235, "height": 315} ], - "crossbow_top": [{"x": 718, "y": 13, "width": 484, "height": 106}], - "fire_side_left": [{"x": 98, "y": 246, "width": 184, "height": 281}], - "fire_side_right": [{"x": 1656, "y": 430, "width": 235, "height": 315}], "fire_top": [ {"x": 532, "y": 17, "width": 188, "height": 97}, - {"x": 1325, "y": 14, "width": 60, "height": 100}, - ], + {"x": 1325, "y": 14, "width": 60, "height": 100} + ] } @@ -69,26 +73,19 @@ def softmax(x: np.ndarray) -> np.ndarray: return e_x / e_x.sum(axis=-1, keepdims=True) -def predict_scene( - session: ort.InferenceSession, - idx_to_class: dict, - image_path: str, - threshold: float = 0.5, -) -> list[str]: +def predict_scene(session: ort.InferenceSession, idx_to_class: dict, image_path: str, threshold: float = 0.5) -> list[ + str]: try: - full_image = Image.open(image_path).convert("RGB") + full_image = Image.open(image_path).convert('RGB') except Exception: return [] if full_image.size != (1920, 1080): return [] - input_name, output_name = ( - session.get_inputs()[0].name, - session.get_outputs()[0].name, - ) + input_name, output_name = session.get_inputs()[0].name, session.get_outputs()[0].name detected_classes = [] for location, boxes in ROI_COORDINATES.items(): for i, box in enumerate(boxes): - x, y, w, h = box["x"], box["y"], box["width"], box["height"] + x, y, w, h = box['x'], box['y'], box['width'], box['height'] roi_pil = full_image.crop((x, y, x + w, y + h)) input_tensor = preprocess_pil_image(roi_pil) outputs = session.run([output_name], {input_name: input_tensor}) @@ -97,7 +94,7 @@ def predict_scene( predicted_index = np.argmax(probabilities) if probabilities[predicted_index] >= threshold: predicted_class = idx_to_class[predicted_index] - if not predicted_class.endswith("_none"): + if not predicted_class.endswith('_none'): detected_classes.append(predicted_class) return detected_classes @@ -106,21 +103,14 @@ def predict_scene( # SECTION 2: 数据清洗主模块 (修改了最后的合并与保存部分) # ============================================================================== - -def clean_data( - file_path, - output_path, - screenshots_base_path, - onnx_model_path, - class_map_path, -): +def clean_data(file_path, output_path, screenshots_base_path, onnx_model_path, class_map_path): print(f"开始清洗数据文件: {file_path}") try: data = pd.read_csv(file_path, header=0) except FileNotFoundError: print(f"错误: 找不到数据文件 '{file_path}'") return - data["original_index"] = data.index + 1 + data['original_index'] = data.index + 1 # --- 原始清洗逻辑部分 (无变更) --- features = data.iloc[:, :-3] labels = data.iloc[:, -3] @@ -129,53 +119,36 @@ def clean_data( # ... (其余清洗逻辑与原脚本完全相同) last_row_features = features.iloc[-1].values last_row_valid = True - if abs(last_row_features[27]) > 6 or abs(last_row_features[61]) > 6: - last_row_valid = False - if np.any(np.abs(last_row_features) >= 100): - last_row_valid = False - if not last_row_valid: - print("错误: 最后一行不满足清洗条件") - return + if abs(last_row_features[27]) > 6 or abs(last_row_features[61]) > 6: last_row_valid = False + if np.any(np.abs(last_row_features) >= 100): last_row_valid = False + if not last_row_valid: print("错误: 最后一行不满足清洗条件"); return last_row = data.iloc[-1].copy() - rows_to_remove = [ - i - for i, row in enumerate(features.values) - if np.any(np.abs(row) >= 100) - ] + rows_to_remove = [i for i, row in enumerate(features.values) if np.any(np.abs(row) >= 100)] cleaned_data = data.drop(rows_to_remove).reset_index(drop=True) - if not (len(data) - 1 in rows_to_remove): - cleaned_data = cleaned_data.iloc[:-1] - if rows_to_remove: - cleaned_data = pd.concat( - [cleaned_data, pd.DataFrame([last_row] * len(rows_to_remove))], - ignore_index=True, - ) - cleaned_data = cleaned_data.drop_duplicates( - subset=cleaned_data.columns[:-3], keep="first" - ).reset_index(drop=True) - features_cleaned, labels_cleaned, pic_names_cleaned = ( - cleaned_data.iloc[:, :-3], - cleaned_data.iloc[:, -3], - cleaned_data.iloc[:, -2], - ) + if not (len(data) - 1 in rows_to_remove): cleaned_data = cleaned_data.iloc[:-1] + if rows_to_remove: cleaned_data = pd.concat([cleaned_data, pd.DataFrame([last_row] * len(rows_to_remove))], + ignore_index=True) + cleaned_data = cleaned_data.drop_duplicates(subset=cleaned_data.columns[:-3], keep='first').reset_index(drop=True) + features_cleaned, labels_cleaned, pic_names_cleaned = cleaned_data.iloc[:, :-3], cleaned_data.iloc[:, + -3], cleaned_data.iloc[:, -2] # ... (异常波动筛选逻辑完全相同) # --- 画面元素识别集成部分 (无变更) --- print("\n开始识别截图中的游戏元素...") try: session = ort.InferenceSession(onnx_model_path) - with open(class_map_path, "r", encoding="utf-8") as f: + with open(class_map_path, 'r', encoding='utf-8') as f: class_to_idx = json.load(f) idx_to_class = {v: k for k, v in class_to_idx.items()} except Exception as e: - print(f"错误:加载模型或class_map文件失败: {e}") + print(f"错误:加载模型或class_map文件失败: {e}"); return grouped_elements = defaultdict(list) for class_name in class_to_idx.keys(): - if class_name.endswith("_none"): + if class_name.endswith('_none'): continue - condensed_name = re.sub(r"_left_", "_", class_name) - condensed_name = re.sub(r"_right_", "_", condensed_name) + condensed_name = re.sub(r'_left_', '_', class_name) + condensed_name = re.sub(r'_right_', '_', condensed_name) grouped_elements[condensed_name].append(class_name) image_feature_columns = sorted(grouped_elements.keys()) print(f"将聚合生成 {len(image_feature_columns)} 个新特征列。") @@ -189,20 +162,14 @@ def clean_data( all_rows_image_data.append(row_image_data) continue try: - detected_full_names = set( - predict_scene(session, idx_to_class, image_path, threshold=0.5) - ) + detected_full_names = set(predict_scene(session, idx_to_class, image_path, threshold=0.5)) row_image_data = {} for condensed_name, full_names in grouped_elements.items(): num_positions = len(full_names) if num_positions == 1: - row_image_data[condensed_name] = ( - 1 if full_names[0] in detected_full_names else 0 - ) + row_image_data[condensed_name] = 1 if full_names[0] in detected_full_names else 0 else: - detections_in_group = [ - fn in detected_full_names for fn in full_names - ] + detections_in_group = [fn in detected_full_names for fn in full_names] num_detected = sum(detections_in_group) if num_detected == num_positions: row_image_data[condensed_name] = 1 @@ -225,9 +192,7 @@ def clean_data( # 1. 检查并拆分155个原始特征为L(77)和R(78)两组 if features_cleaned.shape[1] != 122: - print( - f"警告: 期望122个原始特征,但检测到{features_cleaned.shape[1]}个。将按前61列和剩余列进行分割。" - ) + print(f"警告: 期望122个原始特征,但检测到{features_cleaned.shape[1]}个。将按前61列和剩余列进行分割。") features_L = features_cleaned.iloc[:, :61] features_R = features_cleaned.iloc[:, 61:] @@ -237,43 +202,29 @@ def clean_data( num_r_features = features_R.shape[1] headers_L = [f"{i}L" for i in range(1, 62)] # 1L to 77L - headers_elements_L = [ - f"{i}L" for i in range(62, 62 + num_element_features) - ] + headers_elements_L = [f"{i}L" for i in range(62, 62 + num_element_features)] headers_R = [f"{i}R" for i in range(1, num_r_features + 1)] # 1R to 78R - headers_elements_R = [ - f"{i}R" - for i in range( - num_r_features + 1, num_r_features + 1 + num_element_features - ) - ] + headers_elements_R = [f"{i}R" for i in range(num_r_features + 1, num_r_features + 1 + num_element_features)] # 最终表头顺序 - final_headers = ( - headers_L - + headers_elements_L - + headers_R - + headers_elements_R - + ["Result", "ImgPath"] - ) + final_headers = (headers_L + headers_elements_L + + headers_R + headers_elements_R + + ['Result', 'ImgPath']) # 3. 为Series命名,以便在拼接时作为列名 - labels_cleaned.name = "Result" - pic_names_cleaned.name = "ImgPath" + labels_cleaned.name = 'Result' + pic_names_cleaned.name = 'ImgPath' # 4. 按照新的顺序拼接所有数据部分 - final_cleaned_data = pd.concat( - [ - features_L, # 1L-61L - image_data_df, # 12个元素特征 - features_R, # 1R-61R - image_data_df.copy(), # 12个元素特征 (副本) - labels_cleaned, # label - pic_names_cleaned, # screenshot_filename - ], - axis=1, - ) + final_cleaned_data = pd.concat([ + features_L, # 1L-61L + image_data_df, # 12个元素特征 + features_R, # 1R-61R + image_data_df.copy(), # 12个元素特征 (副本) + labels_cleaned, # label + pic_names_cleaned # screenshot_filename + ], axis=1) # 5. 将新生成的表头赋予DataFrame final_cleaned_data.columns = final_headers @@ -282,25 +233,17 @@ def clean_data( final_cleaned_data.to_csv(output_path, index=False, header=True) print(f"\n清洗和识别后的数据已保存到: {output_path}") - print( - f"最终数据维度: {final_cleaned_data.shape[0]} 行, {final_cleaned_data.shape[1]} 列" - ) + print(f"最终数据维度: {final_cleaned_data.shape[0]} 行, {final_cleaned_data.shape[1]} 列") print(f"已按要求生成自定义表头。") if __name__ == "__main__": # 路径配置与之前保持一致 - input_file = PROJECT_ROOT / "data" / "raw" / "arknights.csv" - output_file = PROJECT_ROOT / "data" / "processed" / "arknights_with_field_recognize_v2.csv" - screenshots_base_path = PROJECT_ROOT / "data" / "images" - - model_dir = PROJECT_ROOT / "models" / "battlefield_recognize" - onnx_model_path = os.path.join(model_dir, "field_recognize.onnx") - class_map_path = os.path.join(model_dir, "class_to_idx.json") - clean_data( - input_file, - output_file, - screenshots_base_path, - onnx_model_path, - class_map_path, - ) + input_file = r"arknights.csv" + output_file = r"arknights_with_field_recognize_v2.csv" + screenshots_base_path = r"images" + + model_dir = r"battlefield_recognize" + onnx_model_path = os.path.join(model_dir, 'field_recognize.onnx') + class_map_path = os.path.join(model_dir, 'class_to_idx.json') + clean_data(input_file, output_file, screenshots_base_path, onnx_model_path, class_map_path) \ No newline at end of file diff --git a/src/tools/data_cleaning_with_field_recognize_gpu.py b/tools/data_cleaning_with_field_recognize_gpu.py similarity index 61% rename from src/tools/data_cleaning_with_field_recognize_gpu.py rename to tools/data_cleaning_with_field_recognize_gpu.py index 2f12699..13ae6af 100644 --- a/src/tools/data_cleaning_with_field_recognize_gpu.py +++ b/tools/data_cleaning_with_field_recognize_gpu.py @@ -9,8 +9,6 @@ import torch.nn as nn from torchvision import models, transforms -from src.core.paths import simulation_path, PROJECT_ROOT - # ============================================================================== # SECTION 1: 游戏画面元素识别模块 (已修改为PyTorch+GPU) # ============================================================================== @@ -19,22 +17,22 @@ "altar_vertical": [ {"x": 910, "y": 174, "width": 95, "height": 104}, {"x": 910, "y": 429, "width": 102, "height": 108}, - {"x": 900, "y": 755, "width": 120, "height": 108}, + {"x": 900, "y": 755, "width": 120, "height": 108} ], "block_parallel": [ {"x": 694, "y": 240, "width": 530, "height": 122}, - {"x": 651, "y": 614, "width": 620, "height": 143}, + {"x": 651, "y": 614, "width": 620, "height": 143} ], "block_vertical": [ {"x": 647, "y": 233, "width": 153, "height": 523}, - {"x": 1112, "y": 239, "width": 159, "height": 514}, + {"x": 1112, "y": 239, "width": 159, "height": 514} ], "coil_narrow": [ {"x": 915, "y": 110, "width": 85, "height": 89}, {"x": 815, "y": 257, "width": 86, "height": 98}, {"x": 1024, "y": 258, "width": 79, "height": 98}, {"x": 790, "y": 643, "width": 97, "height": 102}, - {"x": 1031, "y": 639, "width": 102, "height": 108}, + {"x": 1031, "y": 639, "width": 102, "height": 108} ], "coil_wide": [ {"x": 719, "y": 181, "width": 81, "height": 89}, @@ -44,29 +42,35 @@ {"x": 1159, "y": 757, "width": 93, "height": 92}, {"x": 1257, "y": 533, "width": 94, "height": 102}, {"x": 1236, "y": 344, "width": 85, "height": 97}, - {"x": 1120, "y": 180, "width": 75, "height": 91}, + {"x": 1120, "y": 180, "width": 75, "height": 91} + ], + "crossbow_top": [ + {"x": 718, "y": 13, "width": 484, "height": 106} + ], + "fire_side_left": [ + {"x": 98, "y": 246, "width": 184, "height": 281} + ], + "fire_side_right": [ + {"x": 1656, "y": 430, "width": 235, "height": 315} ], - "crossbow_top": [{"x": 718, "y": 13, "width": 484, "height": 106}], - "fire_side_left": [{"x": 98, "y": 246, "width": 184, "height": 281}], - "fire_side_right": [{"x": 1656, "y": 430, "width": 235, "height": 315}], "fire_top": [ {"x": 532, "y": 17, "width": 188, "height": 97}, - {"x": 1325, "y": 14, "width": 60, "height": 100}, - ], + {"x": 1325, "y": 14, "width": 60, "height": 100} + ] } def predict_scene_pytorch( - model: nn.Module, - idx_to_class: dict, - image_path: str, - transform: transforms.Compose, - device: torch.device, - threshold: float = 0.5, + model: nn.Module, + idx_to_class: dict, + image_path: str, + transform: transforms.Compose, + device: torch.device, + threshold: float = 0.5 ) -> list[str]: """使用PyTorch模型对图片进行场景识别(GPU加速版)""" try: - full_image = Image.open(image_path).convert("RGB") + full_image = Image.open(image_path).convert('RGB') except Exception: return [] @@ -77,7 +81,7 @@ def predict_scene_pytorch( with torch.no_grad(): for location, boxes in ROI_COORDINATES.items(): for i, box in enumerate(boxes): - x, y, w, h = box["x"], box["y"], box["width"], box["height"] + x, y, w, h = box['x'], box['y'], box['width'], box['height'] roi_pil = full_image.crop((x, y, x + w, y + h)) input_tensor = transform(roi_pil).unsqueeze(0).to(device) outputs = model(input_tensor) @@ -87,7 +91,7 @@ def predict_scene_pytorch( if max_prob.item() >= threshold: predicted_class = idx_to_class[predicted_index] - if not predicted_class.endswith("_none"): + if not predicted_class.endswith('_none'): detected_classes.append(predicted_class) return detected_classes @@ -96,21 +100,14 @@ def predict_scene_pytorch( # SECTION 2: 数据清洗主模块 (修改了最后的合并与保存部分) # ============================================================================== - -def clean_data( - file_path, - output_path, - screenshots_base_path, - onnx_model_path, - class_map_path, -): +def clean_data(file_path, output_path, screenshots_base_path, onnx_model_path, class_map_path): print(f"开始清洗数据文件: {file_path}") try: data = pd.read_csv(file_path, header=0) except FileNotFoundError: print(f"错误: 找不到数据文件 '{file_path}'") return - data["original_index"] = data.index + 1 + data['original_index'] = data.index + 1 # --- 原始清洗逻辑部分 (无变更) --- features = data.iloc[:, :-3] @@ -120,35 +117,18 @@ def clean_data( # ... (其余清洗逻辑与原脚本完全相同) last_row_features = features.iloc[-1].values last_row_valid = True - if abs(last_row_features[27]) > 6 or abs(last_row_features[61]) > 6: - last_row_valid = False - if np.any(np.abs(last_row_features) >= 100): - last_row_valid = False - if not last_row_valid: - print("错误: 最后一行不满足清洗条件") - return + if abs(last_row_features[27]) > 6 or abs(last_row_features[61]) > 6: last_row_valid = False + if np.any(np.abs(last_row_features) >= 100): last_row_valid = False + if not last_row_valid: print("错误: 最后一行不满足清洗条件"); return last_row = data.iloc[-1].copy() - rows_to_remove = [ - i - for i, row in enumerate(features.values) - if np.any(np.abs(row) >= 100) - ] + rows_to_remove = [i for i, row in enumerate(features.values) if np.any(np.abs(row) >= 100)] cleaned_data = data.drop(rows_to_remove).reset_index(drop=True) - if not (len(data) - 1 in rows_to_remove): - cleaned_data = cleaned_data.iloc[:-1] - if rows_to_remove: - cleaned_data = pd.concat( - [cleaned_data, pd.DataFrame([last_row] * len(rows_to_remove))], - ignore_index=True, - ) - cleaned_data = cleaned_data.drop_duplicates( - subset=cleaned_data.columns[:-3], keep="first" - ).reset_index(drop=True) - features_cleaned, labels_cleaned, pic_names_cleaned = ( - cleaned_data.iloc[:, :-3], - cleaned_data.iloc[:, -3], - cleaned_data.iloc[:, -2], - ) + if not (len(data) - 1 in rows_to_remove): cleaned_data = cleaned_data.iloc[:-1] + if rows_to_remove: cleaned_data = pd.concat([cleaned_data, pd.DataFrame([last_row] * len(rows_to_remove))], + ignore_index=True) + cleaned_data = cleaned_data.drop_duplicates(subset=cleaned_data.columns[:-3], keep='first').reset_index(drop=True) + features_cleaned, labels_cleaned, pic_names_cleaned = cleaned_data.iloc[:, :-3], cleaned_data.iloc[:, + -3], cleaned_data.iloc[:, -2] # ... (异常波动筛选逻辑完全相同) # --- 画面元素识别集成部分 (修改为PyTorch+GPU) --- @@ -158,11 +138,11 @@ def clean_data( try: # 从ONNX路径生成对应的PTH路径 - pth_model_path = onnx_model_path.replace(".onnx", ".pth") + pth_model_path = onnx_model_path.replace('.onnx', '.pth') print(f"加载PyTorch模型: {pth_model_path}") # 加载类别映射 - with open(class_map_path, "r", encoding="utf-8") as f: + with open(class_map_path, 'r', encoding='utf-8') as f: class_to_idx = json.load(f) idx_to_class = {v: k for k, v in class_to_idx.items()} num_classes = len(class_to_idx) @@ -176,15 +156,11 @@ def clean_data( model.eval() # 定义图像转换 - transform = transforms.Compose( - [ - transforms.Resize((224, 224)), - transforms.ToTensor(), - transforms.Normalize( - mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225] - ), - ] - ) + transform = transforms.Compose([ + transforms.Resize((224, 224)), + transforms.ToTensor(), + transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) + ]) # 重新定义predict_scene函数使用PyTorch模型 def predict_scene(session, idx_to_class, image_path, threshold=0.5): @@ -198,10 +174,10 @@ def predict_scene(session, idx_to_class, image_path, threshold=0.5): grouped_elements = defaultdict(list) for class_name in class_to_idx.keys(): - if class_name.endswith("_none"): + if class_name.endswith('_none'): continue - condensed_name = re.sub(r"_left_", "_", class_name) - condensed_name = re.sub(r"_right_", "_", condensed_name) + condensed_name = re.sub(r'_left_', '_', class_name) + condensed_name = re.sub(r'_right_', '_', condensed_name) grouped_elements[condensed_name].append(class_name) image_feature_columns = sorted(grouped_elements.keys()) print(f"将聚合生成 {len(image_feature_columns)} 个新特征列。") @@ -215,20 +191,14 @@ def predict_scene(session, idx_to_class, image_path, threshold=0.5): all_rows_image_data.append(row_image_data) continue try: - detected_full_names = set( - predict_scene(None, idx_to_class, image_path, threshold=0.5) - ) + detected_full_names = set(predict_scene(None, idx_to_class, image_path, threshold=0.5)) row_image_data = {} for condensed_name, full_names in grouped_elements.items(): num_positions = len(full_names) if num_positions == 1: - row_image_data[condensed_name] = ( - 1 if full_names[0] in detected_full_names else 0 - ) + row_image_data[condensed_name] = 1 if full_names[0] in detected_full_names else 0 else: - detections_in_group = [ - fn in detected_full_names for fn in full_names - ] + detections_in_group = [fn in detected_full_names for fn in full_names] num_detected = sum(detections_in_group) if num_detected == num_positions: row_image_data[condensed_name] = 1 @@ -251,9 +221,7 @@ def predict_scene(session, idx_to_class, image_path, threshold=0.5): # 1. 检查并拆分155个原始特征为L(77)和R(78)两组 if features_cleaned.shape[1] != 122: - print( - f"警告: 期望122个原始特征,但检测到{features_cleaned.shape[1]}个。将按前61列和剩余列进行分割。" - ) + print(f"警告: 期望122个原始特征,但检测到{features_cleaned.shape[1]}个。将按前61列和剩余列进行分割。") features_L = features_cleaned.iloc[:, :61] features_R = features_cleaned.iloc[:, 61:] @@ -263,43 +231,29 @@ def predict_scene(session, idx_to_class, image_path, threshold=0.5): num_r_features = features_R.shape[1] headers_L = [f"{i}L" for i in range(1, 62)] # 1L to 61L - headers_elements_L = [ - f"{i}L" for i in range(62, 62 + num_element_features) - ] + headers_elements_L = [f"{i}L" for i in range(62, 62 + num_element_features)] headers_R = [f"{i}R" for i in range(1, num_r_features + 1)] # 1R to 61R - headers_elements_R = [ - f"{i}R" - for i in range( - num_r_features + 1, num_r_features + 1 + num_element_features - ) - ] + headers_elements_R = [f"{i}R" for i in range(num_r_features + 1, num_r_features + 1 + num_element_features)] # 最终表头顺序 - final_headers = ( - headers_L - + headers_elements_L - + headers_R - + headers_elements_R - + ["Result", "ImgPath"] - ) + final_headers = (headers_L + headers_elements_L + + headers_R + headers_elements_R + + ['Result', 'ImgPath']) # 3. 为Series命名,以便在拼接时作为列名 - labels_cleaned.name = "Result" - pic_names_cleaned.name = "ImgPath" + labels_cleaned.name = 'Result' + pic_names_cleaned.name = 'ImgPath' # 4. 按照新的顺序拼接所有数据部分 - final_cleaned_data = pd.concat( - [ - features_L, # 1L-61L - image_data_df, # 元素特征 - features_R, # 1R-61R - image_data_df.copy(), # 元素特征 (副本) - labels_cleaned, # label - pic_names_cleaned, # screenshot_filename - ], - axis=1, - ) + final_cleaned_data = pd.concat([ + features_L, # 1L-61L + image_data_df, # 元素特征 + features_R, # 1R-61R + image_data_df.copy(), # 元素特征 (副本) + labels_cleaned, # label + pic_names_cleaned # screenshot_filename + ], axis=1) # 5. 将新生成的表头赋予DataFrame final_cleaned_data.columns = final_headers @@ -308,34 +262,24 @@ def predict_scene(session, idx_to_class, image_path, threshold=0.5): final_cleaned_data.to_csv(output_path, index=False, header=True) print(f"\n清洗和识别后的数据已保存到: {output_path}") - print( - f"最终数据维度: {final_cleaned_data.shape[0]} 行, {final_cleaned_data.shape[1]} 列" - ) + print(f"最终数据维度: {final_cleaned_data.shape[0]} 行, {final_cleaned_data.shape[1]} 列") print(f"已按要求生成自定义表头。") if __name__ == "__main__": # 路径配置与之前保持一致 - input_file = PROJECT_ROOT / "data" / "raw" / "arknights.csv" - output_file = PROJECT_ROOT / "data" / "processed" / "arknights_with_field_recognize_v2.csv" - screenshots_base_path = PROJECT_ROOT / "data" / "images" + input_file = r"arknights.csv" + output_file = r"arknights_with_field_recognize_v2.csv" + screenshots_base_path = r"images" - model_dir = PROJECT_ROOT / "models" / "battlefield_recognize" - onnx_model_path = os.path.join(model_dir, "field_recognize.onnx") - class_map_path = os.path.join(model_dir, "class_to_idx.json") + model_dir = r"battlefield_recognize" + onnx_model_path = os.path.join(model_dir, 'field_recognize.onnx') + class_map_path = os.path.join(model_dir, 'class_to_idx.json') # 自动检查PTH文件是否存在 - pth_model_path = onnx_model_path.replace(".onnx", ".pth") + pth_model_path = onnx_model_path.replace('.onnx', '.pth') if not os.path.exists(pth_model_path): print(f"错误: 找不到对应的PTH模型文件 '{pth_model_path}'") - print( - f"请确保存在与ONNX同名的PTH文件: {os.path.basename(pth_model_path)}" - ) + print(f"请确保存在与ONNX同名的PTH文件: {os.path.basename(pth_model_path)}") else: - clean_data( - input_file, - output_file, - screenshots_base_path, - onnx_model_path, - class_map_path, - ) + clean_data(input_file, output_file, screenshots_base_path, onnx_model_path, class_map_path) \ No newline at end of file diff --git a/src/tools/data_nanWriter.py b/tools/data_nanWriter.py similarity index 94% rename from src/tools/data_nanWriter.py rename to tools/data_nanWriter.py index 0dc164a..ddd6ad4 100644 --- a/src/tools/data_nanWriter.py +++ b/tools/data_nanWriter.py @@ -2,13 +2,12 @@ import torch import numpy as np from tqdm import tqdm -from src.models.train import UnitAwareTransformer -from src.core.paths import simulation_path +from train import UnitAwareTransformer # 配置参数(需要与训练时一致) CONFIG = { - "csv_path": str(PROJECT_ROOT / "data" / "raw" / "arknights.csv"), - "model_path": str(PROJECT_ROOT / "models" / "best_model_full.pth"), + "csv_path": "arknights.csv", + "model_path": "models/best_model_full.pth", "max_feature_value": 300, "embed_dim": 128, "num_heads": 8, diff --git a/tools/data_washer_new.py b/tools/data_washer_new.py new file mode 100644 index 0000000..3f10486 --- /dev/null +++ b/tools/data_washer_new.py @@ -0,0 +1,904 @@ +import logging +from pathlib import Path +import cv2 +import numpy as np +import time +import csv +import os +import sys +import recognize +import tqdm + +MONSTER_NUM=56 +black_list_rows = [] + +def merge(nums): + if not nums: + return "" + intervals = [] + start = end = nums[0] + for num in nums[1:]: + if num == end + 1: + end = num + else: + intervals.append((start, end)) + start = end = num + intervals.append((start, end)) # 添加最后一个区间 + + parts = [] + for s, e in intervals: + if s == e: + parts.append(str(s)) + else: + parts.append(f"{s}-{e}") + return ','.join(parts) + +def is_continuous_sublist(sub, main): + return any(sub == main[i:i+len(sub)] for i in range(len(main) - len(sub) + 1)) + +def remove_duplicate_subsequences_easy(listdata, threshold=3): + record = [] + for i in range(len(listdata)-threshold-1): + if is_continuous_sublist(listdata[i+1:i+threshold+2], listdata[:i+threshold+1]): + record.extend(list(range(i+1,i+threshold+2))) + reco_last = list(set(record)) + reco_last.sort() + processed_data = [listdata[j] for j in range(len(listdata)) if j not in reco_last] + return processed_data, reco_last + +def remove_duplicate_subsequences(arr, threshold=3): + """ + 处理二维数组版本,将每行视为独立元素,避免内存溢出 + :param arr: 二维np数组,形状为(N, D) + :param threshold: 需要删除的连续重复子序列最小长度 + :return: 去重后的数组,被删除的索引列表 + """ + if arr.ndim != 2: + raise ValueError("输入必须是二维数组") + + n = arr.shape[0] + print(arr[9]) + # 哈希化每行以便快速比较 + dtype = np.dtype((np.void, arr.dtype.itemsize * arr.shape[1])) + hashed_arr = np.ascontiguousarray(arr).view(dtype).flatten() + + # 初始化滚动数组和列最大值记录 + prev_row = np.zeros(n, dtype=int) + max_per_col = np.zeros(n, dtype=int) + targ = 10 + for i in range(n): + # 生成当前行比较掩码 + equal_mask = (hashed_arr[i] == hashed_arr) + + # 计算当前行DP值 + curr_row = np.zeros(n, dtype=int) + curr_row[0] = equal_mask[0] # 处理j=0 + + if i > 0: + # 向量化计算j>=1的情况 + curr_row[1:] = np.where(equal_mask[1:], prev_row[:-1] + 1, 0) + + # 更新列最大值(只考虑i<=j的情况) + col_mask = np.arange(n) > i + max_per_col = np.maximum(max_per_col, curr_row * col_mask) + + # 滚动更新 + prev_row = curr_row + if i*100/n >= targ: + print("检查重复数据,处理进度: {:.1f}%: ".format(i*100/n)) + targ += 10 + + # 确定有效列并生成删除索引 + valid_cols = np.where(max_per_col >= threshold)[0] + to_remove = set() + + for j in valid_cols: + length = max_per_col[j] + start = max(0, j - length) + to_remove.update(range(start, j+1)) + + final_indices = sorted(to_remove) + return np.delete(arr, final_indices, axis=0), final_indices + +def isfloat(value): + try: + float(value) + return True + except ValueError: + return False + + +def read_and_remove_zeros(filename,MONSTER_NUM=56): + ''' + 输入数据文件名 + 输出去0和空之后的数组,和删去的行号列表 + ''' + data = [] + datafull = [] + row_id = 0 + kong = [] + short = [] + lines_num = MONSTER_NUM*2 + with open(filename, 'r') as file: + csv_reader = csv.reader(file) + for row in csv_reader: + if len(row) < lines_num + 1: + short.append(row_id) + data.append([0]*lines_num) + datafull.append([0]*lines_num) + # 分离数字部分和末尾的字母 + elif isfloat(row[0]) and '' not in row[:lines_num]: + numbers = list(map(int,map(float, row[:lines_num]))) # 转换为整数列表 + vals = row[lines_num:] + datafull.append(numbers+vals) + data.append(numbers) + else: + kong.append(row_id) + data.append([0]*lines_num) + datafull.append([0]*lines_num) + #把这一行转化为全0行暂时录入 + row_id += 1 + print('原数据总长度:',len(datafull)) + print('数据长度过短的行:',merge(short)) + print('含有不合法数据的行:',merge(kong)) + + np_array = np.array(data) + # 去除全零行 + all_zeros = [] + for i in range(np_array.shape[0]): + if np.all(np_array[i][MONSTER_NUM:] == 0) or np.all(np_array[i][:MONSTER_NUM] == 0): + all_zeros.append(i) + #np.delete(np_array, all_zeros, axis=0) + + data_new = [datafull[j] for j in range(len(datafull)) if j not in all_zeros] + + all_zeros_idx = [i for i in all_zeros if i not in kong+short] + print('一侧数据全为0的行:',merge(all_zeros_idx)) + print('筛选后数据总长度:',len(data_new)) + return data_new,all_zeros,len(datafull) + +def do_duplicate(listdata): + if listdata == []: + return [],[] + num_data = [list(map(int,map(float, i[:MONSTER_NUM*2]))) for i in listdata] + np_num_data = np.array(num_data) + _,remove_list = remove_duplicate_subsequences(np_num_data, threshold=3) + #print(remove_list) + result = [listdata[j] for j in range(len(listdata)) if j not in remove_list] + return result,remove_list + +def ori_pos(n,del1,del2): + remaining_after_first = [i for i in range(n) if i not in del1] + second_deleted_original = [remaining_after_first[t] for t in del2] + all_deleted = del1 + second_deleted_original + all_deleted.sort() + return all_deleted,second_deleted_original + +def view_monster_counts(listdata): + if listdata == []: + return True,[],[] + wrong_counts = [] + num_left = [list(map(int,map(float, i[:MONSTER_NUM]))) for i in listdata] + num_right = [list(map(int,map(float, i[MONSTER_NUM:MONSTER_NUM*2]))) for i in listdata] + print(len(num_left[0]),len(num_right[0])) + black_listed = False + MONSTER_MIN = 0 + MONSTER_MAX = 100 + MONSTER_LIMIT = { + 0:[range(0,100),'狗',False], + 1:[range(0,50),'红虫',False], + 2:[range(0,30),'大盾',False], + 3:[range(0,30),'大剑',False], + 6:[range(0,6),'庞贝',False], + 8:[range(0,4),'石头人',False], + 27:[range(0,4),'杰斯顿',False], + 28:[[0],'自在',True], + 29:[[0],'狼主',True], + 30:[[0],'雷德',True] #三大boss全设为0 + } + ind = 0 + for i1,i2 in zip(num_left,num_right): + for x1 in i1: + if x1 < MONSTER_MIN: + print(f'{ind}行左侧发现小于0的数据!') + if ind not in wrong_counts: + wrong_counts.append(ind) + if x1 > MONSTER_MAX: + print(f'{ind}行左侧发现大于100的数据!') + if ind not in wrong_counts: + wrong_counts.append(ind) + for x2 in i2: + if x2 < MONSTER_MIN: + print(f'{ind}行右侧发现小于0的数据!') + if ind not in wrong_counts: + wrong_counts.append(ind) + if x2 > MONSTER_MAX: + print(f'{ind}行右侧发现大于100的数据!') + if ind not in wrong_counts: + wrong_counts.append(ind) + for j in MONSTER_LIMIT: + if i1[j] not in MONSTER_LIMIT[j][0]: + print(f'{ind}行左侧发现{MONSTER_LIMIT[j][1]},数量:{i1[j]}') + if ind not in wrong_counts: + wrong_counts.append(ind) + if MONSTER_LIMIT[j][2] and not black_listed: + black_listed = True + print(f'确认为30人数据,文档加入黑名单。') + if i2[j] not in MONSTER_LIMIT[j][0]: + print(f'{ind}行右侧发现{MONSTER_LIMIT[j][1]},数量:{i2[j]}') + if ind not in wrong_counts: + wrong_counts.append(ind) + if MONSTER_LIMIT[j][2] and not black_listed: + black_listed = True + print(f'确认为30人数据,文档加入黑名单。') + ind += 1 + processed_data = [listdata[j] for j in range(len(listdata)) if j not in wrong_counts] + mwdata = is_list_true_np(processed_data) + processed_data2 = [processed_data[j] for j in range(len(processed_data)) if j not in mwdata] + print(f'怪物信息不符合权重分配的数据行:{mwdata}') + return black_listed,wrong_counts,mwdata,processed_data2 + +def del_duplicate_by_time(listdata,delete_no_time = True): + ind = 0 + no_time = [] + timedata = [] + wrong_time = [] + for i in listdata: + if len(i) < MONSTER_NUM*2+2 or i[-1] == 'N/A': + no_time.append(ind) + ind += 1 + print(merge(no_time),'行:未发现时间戳!') + data_with_time = [listdata[j] for j in range(len(listdata)) if j not in no_time] + ind = 0 + for i in data_with_time: + if i[-1] not in timedata: + timedata.append(i[-1]) + else: + wrong_time.append(ind) + print(f'{timedata.index(i[-1])}行与{ind}行发现同名截图,文件名:{i[-1]}') + ind += 1 + if not delete_no_time: + #先找到wrongtime元素的原始位置,再从原列表删除 + remaining_after_first = [i for i in range(len(listdata)) if i not in no_time] + second_deleted_original = [remaining_after_first[t] for t in wrong_time] + data_with_time_ok = [listdata[j] for j in range(len(listdata)) if j not in second_deleted_original] + wrong_time = second_deleted_original + else: + data_with_time_ok = [data_with_time[j] for j in range(len(data_with_time)) if j not in wrong_time] + return data_with_time_ok, no_time, wrong_time + +def savecsv(listdata,outputfile): + # 处理数字转换 + processed = [] + for row in listdata: + new_row = [] + for item in row: + if isinstance(item, (int, float)): + new_row.append(int(item)) + else: + new_row.append(item) + processed.append(new_row) + + # 写入CSV文件 + with open(outputfile, 'w', newline='') as f: + csv.writer(f).writerows(processed) + print(f'已保存至{outputfile}') + + +def find_csv_files(root_dir): + csv_files = [] + for root, dirs, files in os.walk(root_dir): + for file in files: + if file.lower().endswith('.csv'): + csv_path = os.path.join(root, file) + csv_files.append(csv_path) + return csv_files + +def easydata2data(easydata): + # 测试用函数 + # easydata格式:[[[序号,数量][序号,数量][序号,数量]],[[序号,数量][序号,数量][序号,数量]],结果],没有的序号和数量留-1,序号是真实序号 + datalist = [0]*MONSTER_NUM*2 + for i in easydata[0]: + if i[0] > 0: + datalist[i[0]-1] = i[1] + for i in easydata[1]: + if i[0] > 0: + datalist[i[0]-1+MONSTER_NUM] = i[1] + datalist.extend(easydata[2]) + return datalist + +def find_where_from(easydata,floder_path): + # easydata格式:[[[序号,数量][序号,数量][序号,数量]],[[序号,数量][序号,数量][序号,数量]],结果],没有的序号和数量留-1,序号是真实序号 + datalist = [0]*MONSTER_NUM*2 + from_list = [] + for i in easydata[0]: + if i[0] > 0: + datalist[i[0]-1] = i[1] + for i in easydata[1]: + if i[0] > 0: + datalist[i[0]-1+MONSTER_NUM] = i[1] + datalist.extend(easydata[2]) + print(datalist) + csvlist = find_csv_files(floder_path) + for c in csvlist: + print(c) + for c in csvlist: + print(f'正在检查:{c}…………………………') + lines_num = MONSTER_NUM*2 + datafull = [] + row_id = 0 + with open(c, 'r') as file: + csv_reader = csv.reader(file) + for row in csv_reader: + if isfloat(row[0]) and '' not in row[:lines_num] and len(row) > lines_num: + numbers = list(map(int,map(float, row[:lines_num]))) # 转换为整数列表 + vals = [row[lines_num]] + if datalist == numbers+vals: + from_list.append([c,row_id+1]) + print(f'数据来源于:{c},第{row_id+1}行!') + row_id += 1 + str_from = '' + if from_list != []: + str_from = "\n".join([i[0] + '第:' + str(i[1]) + '行' for i in from_list]) + print(f'可能的数据来源:{str_from}') + +def is_distance_not_over_60(a,b,c,d): + #a, b = interval1 + #c, d = interval2 + # 判断区间是否有交集 + if max(a, c) <= min(b, d): + return True # 有交集时距离为0,未超过60 + # 计算不重叠时的间隔 + if b < c: + distance = c - b # interval1在左,interval2在右 + else: + distance = a - d # interval2在左,interval1在右 + return distance <= 60 + +def is_list_true_np(fulllist): + cost_list = [ + [2,0], [2,0.1], [7,0], [7,0], [3,0.1], [10,0], [25,15], [22,0], [25,100], [7,0], + [5,0.2], [7,0], [2,0], [15,2], [13,0], [12,1.5], + [6,0.2], [18,3], [15,3], [18,1], [11,0], [10,1], [16,0], [5,0.5], [15,2], [14,0], + [30,0], [35,100], [-1,-1], [-1,-1], [-1,-1], [6,0.5], [6,0], + [16,0], [15,5], [11,0], [26,0], [15,0], [4,0.1], [10,0], [21,0], [5,0.2], + [18,0], [9,1.5], [8,0.5], [16,0], [21,0], [7,0], [36,10], [10,2], [30,15], + [25,0], [27,0], [32,6], [25,50], [15,5] + ] + round_cost_list = [[50,70],[70,90],[90,110],[110,130],[120,160],[140,180],[160,200],[170,230],[190,250],[210,270]] + + # Convert to numpy arrays + cost_arr = np.array(cost_list) + round_cost_arr = np.array(round_cost_list) + round_low = round_cost_arr[:, 0] + round_high = round_cost_arr[:, 1] + + # Validity mask for cost entries not equal to [-1, -1] + valid_mask = np.all(cost_arr != [-1, -1], axis=1) + + # Split the input into left and right parts + fulllist_np = np.array([i[:112] for i in fulllist], dtype=np.float64) + N = fulllist_np.shape[0] + left_part = fulllist_np[:, :56] + right_part = fulllist_np[:, 56:112] + + # Calculate valid entries (cost not [-1,-1] and count >0) + valid_left = valid_mask[np.newaxis, :] & (left_part > 0) + valid_right = valid_mask[np.newaxis, :] & (right_part > 0) + + # Compute mincostL and maxcostL for left + left_min_terms = ( + (left_part - 1) * cost_arr[np.newaxis, :, 0] + + ((left_part - 1) * (left_part - 2) * cost_arr[np.newaxis, :, 1]) / 2 + + 0.01 + ) * valid_left + mincostL = left_min_terms.sum(axis=1) + + left_max_terms = ( + (left_part + 1) * cost_arr[np.newaxis, :, 0] + + (left_part * (left_part + 1) * cost_arr[np.newaxis, :, 1]) / 2 - + 0.01 + ) * valid_left + maxcostL = left_max_terms.sum(axis=1) + + # Compute mincostR and maxcostR for right + right_min_terms = ( + (right_part - 1) * cost_arr[np.newaxis, :, 0] + + ((right_part - 1) * (right_part - 2) * cost_arr[np.newaxis, :, 1]) / 2 + + 0.01 + ) * valid_right + mincostR = right_min_terms.sum(axis=1) + + right_max_terms = ( + (right_part + 1) * cost_arr[np.newaxis, :, 0] + + (right_part * (right_part + 1) * cost_arr[np.newaxis, :, 1]) / 2 - + 0.01 + ) * valid_right + maxcostR = right_max_terms.sum(axis=1) + + # Check overlap with round costs + left_low = np.maximum(mincostL[:, np.newaxis], round_low) + left_high = np.minimum(maxcostL[:, np.newaxis], round_high) + left_cond = left_low <= left_high + + right_low = np.maximum(mincostR[:, np.newaxis], round_low) + right_high = np.minimum(maxcostR[:, np.newaxis], round_high) + right_cond = right_low <= right_high + + both_cond = left_cond & right_cond + any_round = np.any(both_cond, axis=1) + + # Get indices where no round condition is satisfied + false_indices = np.where(~any_round)[0].tolist() + return false_indices + +def is_list_true(onelist): + roundlist = [] + #费用和附加费用,写死在代码里吧,不想读文件了。 + cost_list = [[2,0],[2,0.1],[7,0],[7,0],[3,0.1],[10,0],[25,15],[22,0],[25,100],[7,0],[5,0.2],[7,0],[2,0],[15,2],[13,0],[12,1.5], + [6,0.2],[18,3],[15,3],[18,1],[11,0],[10,1],[16,0],[5,0.5],[15,2],[14,0],[30,0],[35,100],[-1,-1],[-1,-1],[-1,-1],[6,0.5],[6,0], + [16,0],[15,5],[11,0],[26,0],[15,0],[4,0.1],[10,0],[21,0],[5,0.2],[18,0],[9,1.5],[8,0.5],[16,0],[21,0],[7,0],[36,10],[10,2],[30,15], + [25,0],[27,0],[32,6],[25,50],[15,5]] + # + round_cost_list = [[50,70],[70,90],[90,110],[110,130],[120,160],[140,180],[160,200],[170,230],[190,250],[210,270]] + left = onelist[:MONSTER_NUM] + right = onelist[MONSTER_NUM:MONSTER_NUM*2] + #result = onelist[MONSTER_NUM*2] + #print(left,right,result) + mincostL = sum([(left[i]-1)*cost_list[i][0]+(left[i]-1)*(left[i]-2)*cost_list[i][1]/2+0.01 for i in range(len(left)) if (cost_list[i] != [-1,-1]) and (left[i] > 0)]) + maxcostL = sum([(left[i]+1)*cost_list[i][0]+left[i]*(left[i]+1)*cost_list[i][1]/2-0.01 for i in range(len(left)) if (cost_list[i] != [-1,-1]) and (left[i] > 0)]) + mincostR = sum([(right[i]-1)*cost_list[i][0]+(right[i]-1)*(right[i]-2)*cost_list[i][1]/2+0.01 for i in range(len(right)) if (cost_list[i] != [-1,-1]) and (right[i] > 0)]) + maxcostR = sum([(right[i]+1)*cost_list[i][0]+right[i]*(right[i]+1)*cost_list[i][1]/2-0.01 for i in range(len(right)) if (cost_list[i] != [-1,-1]) and (right[i] > 0)]) + print(mincostL,maxcostL,mincostR,maxcostR) + for i in range(len(round_cost_list)): + if max(mincostL, round_cost_list[i][0]) <= min(maxcostL, round_cost_list[i][1]) and max(mincostR, round_cost_list[i][0]) <= min(maxcostR, round_cost_list[i][1]): + roundlist.append(i) + if roundlist != []: + return True + else: + print(f'{onelist}is not true!!!') + return False + #return is_distance_not_over_60(mincostL,maxcostL,mincostR,maxcostR) + +def recognize_review(data,img_floder,matched_threshold = 0.1,ocr_threshold = 0.5): + print("正在进行识别数据检查") + print("data行数:",len(data)) + ref_row = [0] * (recognize.MONSTER_COUNT * 2) + need_delete = [False] * len(data) + for idx, row in tqdm.tqdm(enumerate(data), total=len(data), desc="Processing rows"): + ref_row = [0] * (recognize.MONSTER_COUNT * 2) + try: + img_name = row[recognize.MONSTER_COUNT * 2 + 1] + img_path = img_floder / Path(img_name) + if not img_path.exists(): + print(f"未找到对应的图像: {img_name} ") + continue + img = cv2.imread(img_path) + main_roi = ((0, 0), (img.shape[1], img.shape[0])) + results = recognize.process_regions(main_roi, img,matched_threshold,ocr_threshold) + # 处理结果 + for res in results: + if "error" in res: + print(f"识别失败 行号: {idx}, 图片: {img_name}, 错误类型: {res['error']}", file=sys.stderr) + break + if res["matched_id"]: + if res["region_id"] < 3: + ref_row[res["matched_id"] - 1] = int(res["number"]) + else: + ref_row[res["matched_id"] - 1 + MONSTER_NUM] = int(res["number"]) + else: + # 检查数据行是否与参考行匹配 + data_row = row[0 : recognize.MONSTER_COUNT * 2] + if data_row != ref_row: + print(f"找到不匹配的数据行: {idx} 行,对应图片文件: {img_name}", file=sys.stderr) + print(f"识别结果 : {ref_row}", file=sys.stderr) + print(f"文件数据 : {data_row}", file=sys.stderr) + need_delete[idx] = True + else: + need_delete[idx] = False + except Exception as e: + logging.exception(f"Error processing line {idx}", e) + need_delete[idx] = True + newdata = [row for idx, row in enumerate(data) if not need_delete[idx]] + deleted = [idx for idx, del_flag in enumerate(need_delete) if del_flag] + return newdata, deleted + +#newdata,deleted,ori_len = read_and_remove_zeros('0502.csv',MONSTER_NUM=56) +#_,inc = remove_duplicate_subsequences() +#print('数据例:',newdata[:3]) +#result,deleted2 = do_duplicate(newdata) +#print(deleted,deleted2) +#dt = ori_pos(ori_len,deleted,deleted2) +#print(dt) +#print('筛选后数据总长度:',len(result)) +#view_monster_counts(newdata) +#del_duplicate_by_time(newdata) + +def process_full(filename,do_remove_duplicate_subsequences = False,delete_no_time = True,open_black_list = True,re_recognize_imgs = False,img_floder = '',matched_threshold=0.1, ocr_threshold=0.5): + wrong_type_list = [] + newdata,deleted0,ori_len = read_and_remove_zeros(filename,MONSTER_NUM=56) + deleted1 = [] + if do_remove_duplicate_subsequences: + newdata,deleted1 = do_duplicate(newdata) + newdata, deleted2, deleted3 = del_duplicate_by_time(newdata,delete_no_time) + if not delete_no_time: + deleted2 = [] + black_listed,deleted4,deleted5,newdata = view_monster_counts(newdata) + deleted6 = [] + if re_recognize_imgs: + newdata, deleted6 = recognize_review(newdata,img_floder,matched_threshold, ocr_threshold) + deleted7 = [] + if open_black_list: + newdata,deleted7 = process_black_list(newdata) + + dl = deleted0 + flag = 0 + for i in [deleted1,deleted2,deleted3,deleted4,deleted5,deleted6,deleted7]: + if i != []: + dl,secori = ori_pos(ori_len,dl,i) + if flag == 0: + wrong_type_list.append(['不合法的数据:',merge([i + 1 for i in deleted0])]) + wrong_type_list.append(['重复出现的连续数据*:',merge([i + 1 for i in secori])]) + elif flag == 1: + wrong_type_list.append(['未包含时间轴的数据:',merge([i + 1 for i in secori])]) + elif flag == 2: + wrong_type_list.append(['时间轴信息重复的数据:',merge([i + 1 for i in secori])]) + elif flag == 3: + wrong_type_list.append(['怪物信息错误的数据:',merge([i + 1 for i in secori])]) + elif flag == 4: + wrong_type_list.append(['不符合出怪权重规则的数据:',merge([i + 1 for i in secori])]) + elif flag == 5: + wrong_type_list.append(['经图片识别错误的数据*:',merge([i + 1 for i in secori])]) + elif flag == 6: + wrong_type_list.append(['黑名单内数据:',merge([i + 1 for i in secori])]) + flag += 1 + return black_listed,newdata,dl,wrong_type_list + + +def test1(): + black_listed,newdata,dl,wrong_type_list = process_full('0502processed.csv') + dllist = [i + 1 for i in dl] + print(f'删除了{dllist}行的数据') + for i in wrong_type_list: + print(i) + savecsv(newdata,'0502processed2.csv') + +def process_floder(flodername,savefilename,lastsavefilename,do_remove_duplicate_subsequences = True,delete_no_time = True,open_black_list = True,re_recognize_imgs = False,img_floder = '',matched_threshold=0.1, ocr_threshold=0.5): + ''' + 输入: + flodername:需要处理的文件夹名 + savefilename:全部整合保存到的文件名(不进行总去重) + lastsavefilename:全部整合并去重保存到的最终文件名 + do_remove_duplicate_subsequences:是否清理连续3个以上重复元素的重复序列 + delete_no_time:是否删除没有时间戳的数据行 + ''' + global black_list_rows + full_data_list = [] + csvlist = find_csv_files(flodername) + for csv in csvlist: + print(csv) + for csv in csvlist: + print(f'正在处理:{csv}…………………………') + black_listed,newdata,dl,wrong_type_list = process_full(csv,do_remove_duplicate_subsequences,delete_no_time,open_black_list,re_recognize_imgs,img_floder,matched_threshold, ocr_threshold) + dllist = [i + 1 for i in dl] + print(f'删除了{merge(dllist)}行的数据') + for i in wrong_type_list: + print(i) + if not black_listed: + #未进黑名单则合并至全部数据 + full_data_list += newdata + else: + print(f'该数据为30人局数据,自动进入黑名单,不计入总数据!') + if len(newdata) < 5000:#不是整合数据 + black_list_rows += newdata + savecsv(full_data_list,savefilename) + black_listed,newdata,dl,wrong_type_list = process_full(savefilename,do_remove_duplicate_subsequences,delete_no_time,open_black_list,re_recognize_imgs,img_floder,matched_threshold, ocr_threshold) + #保存后再总处理去重 + dllist = [i + 1 for i in dl] + print(f'删除了{merge(dllist)}行的数据') + for i in wrong_type_list: + print(i) + savecsv(newdata,lastsavefilename) + +def process_black_list(full_data): + #黑名单里所有的数据检测到重复的就删 + global black_list_rows + delete_rows = [] + ok_data = [] + idx = 0 + for i in full_data: + if i in black_list_rows: + delete_rows.append(idx) + else: + ok_data.append(i) + idx += 1 + print(f'黑名单内数据:{merge(delete_rows)}') + return ok_data,delete_rows + + +def process_file(filename,savefilename,do_remove_duplicate_subsequences = True,delete_no_time = True,open_black_list = True,re_recognize_imgs = False,img_floder = '',matched_threshold=0.1, ocr_threshold=0.5): + ''' + 输入: + filename:需要处理的文件名 + savefilename:处理后保存到的文件名 + do_remove_duplicate_subsequences:是否清理连续3个以上重复元素的重复序列 + delete_no_time:是否删除没有时间戳的数据行 + ''' + black_listed,newdata,dl,wrong_type_list = process_full(filename,do_remove_duplicate_subsequences,delete_no_time,open_black_list,re_recognize_imgs,img_floder,matched_threshold, ocr_threshold) + #保存后再总处理去重 + dllist = [i + 1 for i in dl] + print(f'删除了{merge(dllist)}行的数据') + for i in wrong_type_list: + print(i) + savecsv(newdata,savefilename) + + +#process_floder(r'D:\Backup\Downloads\arcdata','arcdata_fullaa.csv','arcdata_full_washed_plus.csv') + +import tkinter as tk +from tkinter import ttk, filedialog, messagebox +import sys +import threading +import queue + +class RedirectText(object): + def __init__(self, text_widget, log_file="processing.log"): + self.text_widget = text_widget + self.log_file = log_file + self.queue = queue.Queue() + self.root = text_widget.master + self.lock = threading.Lock() + + # 初始化日志文件 + self.setup_logfile() + + def setup_logfile(self): + try: + # 使用追加模式打开日志文件 + self.log_fd = open(self.log_file, "a", encoding="utf-8") + except Exception as e: + self.log_fd = None + self.write(f"无法打开日志文件: {str(e)}\n") + + def write(self, message): + # 写入日志文件(带线程锁) + with self.lock: + if self.log_fd: + try: + self.log_fd.write(message) + self.log_fd.flush() # 确保立即写入磁盘 + except Exception as e: + self.log_fd = None + self.queue.put(f"日志写入失败: {str(e)}\n") + + # 写入队列供界面显示 + self.queue.put(message) + self.root.after(100, self.update_text) + + def update_text(self): + while not self.queue.empty(): + msg = self.queue.get_nowait() + self.text_widget.insert(tk.END, msg) + self.text_widget.see(tk.END) + + def flush(self): + pass + + def close_logfile(self): + with self.lock: + if self.log_fd: + self.log_fd.close() + self.log_fd = None + +class ProcessingThread(threading.Thread): + def __init__(self, func, args=(), kwargs={}, callback=None): + super().__init__() + self.func = func + self.args = args + self.kwargs = kwargs + self.callback = callback + self.daemon = True + self.exception = None + + def run(self): + try: + self.func(*self.args, **self.kwargs) + except Exception as e: + self.exception = e + finally: + if self.callback: + self.callback(self.exception) + + +def create_gui(): + root = tk.Tk() + root.title("数据搅拌机") + root.geometry("800x600") + + # 在此处定义关闭事件处理函数(推荐位置) + def on_close(): + sys.stdout.close_logfile() # 关闭日志文件 + if messagebox.askokcancel("退出", "确定要退出程序吗?"): # 添加确认对话框 + root.destroy() # 销毁窗口 + + # 绑定关闭事件处理 + root.protocol("WM_DELETE_WINDOW", on_close) + + # 创建文本输出区域 + output_text = tk.Text(root, wrap=tk.WORD) + output_text.grid(row=3, column=0, columnspan=2, padx=10, pady=10, sticky="nsew") + sys.stdout = RedirectText(output_text, "data_processing.log") + + # 处理文件夹的Frame + folder_frame = ttk.LabelFrame(root, text="处理文件夹(处理文件夹及其所有子文件夹下的CSV文件,并合并为一个)") + folder_frame.grid(row=0, column=0, padx=10, pady=5, sticky="ew") + + # 处理文件夹的组件 + ttk.Label(folder_frame, text="选择文件夹:").grid(row=0, column=0, padx=5, sticky="w") + folder_path = tk.StringVar() + folder_entry = ttk.Entry(folder_frame, textvariable=folder_path, width=40) + folder_entry.grid(row=0, column=1, padx=5) + ttk.Button(folder_frame, text="浏览", command=lambda: folder_path.set(filedialog.askdirectory())).grid(row=0, column=2, padx=5) + + ttk.Label(folder_frame, text="中间保存文件(不进行最终去重):").grid(row=1, column=0, padx=5, sticky="w") + interim_save = tk.StringVar() + ttk.Entry(folder_frame, textvariable=interim_save, width=40).grid(row=1, column=1, padx=5) + ttk.Button(folder_frame, text="浏览", command=lambda: interim_save.set(filedialog.asksaveasfilename(defaultextension=".csv",filetypes=[("CSV文件", "*.csv"), ("所有文件", "*.*")]))).grid(row=1, column=2, padx=5) + + ttk.Label(folder_frame, text="最终保存文件:").grid(row=2, column=0, padx=5, sticky="w") + final_save = tk.StringVar() + ttk.Entry(folder_frame, textvariable=final_save, width=40).grid(row=2, column=1, padx=5) + ttk.Button(folder_frame, text="浏览", command=lambda: final_save.set(filedialog.asksaveasfilename(defaultextension=".csv",filetypes=[("CSV文件", "*.csv"), ("所有文件", "*.*")]))).grid(row=2, column=2, padx=5) + + # 复选框 + remove_dup = tk.BooleanVar(value=False) + ttk.Checkbutton(folder_frame, text="不依赖时间戳清理重复子序列(在大数据集会非常慢,通常关闭)", variable=remove_dup).grid(row=3, column=0, columnspan=3, sticky="w") + + del_time = tk.BooleanVar(value=True) + ttk.Checkbutton(folder_frame, text="删除无时间戳数据", variable=del_time).grid(row=4, column=0, columnspan=3, sticky="w") + + open_black = tk.BooleanVar(value=True) + ttk.Checkbutton(folder_frame, text="将黑名单文件内的所有数据行同时加入黑名单", variable=open_black).grid(row=5, column=0, columnspan=3, sticky="w") + + # 修改后的图片识别行(将复选框和阈值输入放在同一行) + re_recognize_var = tk.BooleanVar(value=False) + ttk.Checkbutton(folder_frame, text="启用图片二次识别(必须指定图片路径)", variable=re_recognize_var).grid(row=6, column=0, padx=5, sticky="w") + + # 添加匹配阈值设置 + ttk.Label(folder_frame, text="匹配阈值:").grid(row=6, column=1, padx=(20,5), sticky="e") + matched_threshold_var = tk.DoubleVar(value=0.1) + ttk.Entry(folder_frame, textvariable=matched_threshold_var, width=6).grid(row=6, column=2, sticky="w") + + # 添加OCR阈值设置 + ttk.Label(folder_frame, text="OCR阈值:").grid(row=6, column=3, padx=(20,5), sticky="e") + ocr_threshold_var = tk.DoubleVar(value=0.5) + ttk.Entry(folder_frame, textvariable=ocr_threshold_var, width=6).grid(row=6, column=4, sticky="w") + + # 调整后续行号(原row=6改为row=7开始) + ttk.Label(folder_frame, text="图片文件夹路径:").grid(row=7, column=0, padx=5, sticky="w") + img_folder_path = tk.StringVar() + ttk.Entry(folder_frame, textvariable=img_folder_path, width=40).grid(row=7, column=1, padx=5) + ttk.Button(folder_frame, text="浏览", command=lambda: img_folder_path.set(filedialog.askdirectory())).grid(row=7, column=2, padx=5) + + # 调整处理文件夹按钮的行号 + + + # 处理文件夹按钮 + folder_button = ttk.Button(folder_frame, text="执行处理") + folder_button.grid(row=8, column=0, columnspan=5, pady=5) + + # 处理文件的Frame + file_frame = ttk.LabelFrame(root, text="处理单个文件") + file_frame.grid(row=1, column=0, padx=10, pady=5, sticky="ew") + + # 处理文件的组件 + ttk.Label(file_frame, text="选择文件:").grid(row=0, column=0, padx=5, sticky="w") + file_path = tk.StringVar() + ttk.Entry(file_frame, textvariable=file_path, width=40).grid(row=0, column=1, padx=5) + ttk.Button(file_frame, text="浏览", command=lambda: file_path.set(filedialog.askopenfilename())).grid(row=0, column=2, padx=5) + + ttk.Label(file_frame, text="保存路径:").grid(row=1, column=0, padx=5, sticky="w") + save_path = tk.StringVar() + ttk.Entry(file_frame, textvariable=save_path, width=40).grid(row=1, column=1, padx=5) + ttk.Button(file_frame, text="浏览", command=lambda: save_path.set(filedialog.asksaveasfilename(defaultextension=".csv",filetypes=[("CSV文件", "*.csv"), ("所有文件", "*.*")]))).grid(row=1, column=2, padx=5) + + # 复选框 + remove_dup_file = tk.BooleanVar(value=False) + ttk.Checkbutton(file_frame, text="不依赖时间戳清理重复子序列(在大数据集会非常慢,通常关闭)", variable=remove_dup_file).grid(row=2, column=0, columnspan=3, sticky="w") + + del_time_file = tk.BooleanVar(value=True) + ttk.Checkbutton(file_frame, text="删除无时间戳数据", variable=del_time_file).grid(row=3, column=0, columnspan=3, sticky="w") + + open_black_file = tk.BooleanVar(value=True) + ttk.Checkbutton(file_frame, text="将黑名单文件内的所有数据行同时加入黑名单", variable=open_black_file).grid(row=4, column=0, columnspan=3, sticky="w") + + # 修改后的图片识别行 + re_recognize_file_var = tk.BooleanVar(value=False) + ttk.Checkbutton(file_frame, text="启用图片二次识别(必须指定图片路径)", variable=re_recognize_file_var).grid(row=5, column=0, padx=5, sticky="w") + + # 匹配阈值 + ttk.Label(file_frame, text="匹配阈值:").grid(row=5, column=1, padx=(20,5), sticky="e") + matched_threshold_file_var = tk.DoubleVar(value=0.1) + ttk.Entry(file_frame, textvariable=matched_threshold_file_var, width=6).grid(row=5, column=2, sticky="w") + + # OCR阈值 + ttk.Label(file_frame, text="OCR阈值:").grid(row=5, column=3, padx=(20,5), sticky="e") + ocr_threshold_file_var = tk.DoubleVar(value=0.5) + ttk.Entry(file_frame, textvariable=ocr_threshold_file_var, width=6).grid(row=5, column=4, sticky="w") + + # 调整后续行号 + ttk.Label(file_frame, text="图片文件夹路径:").grid(row=6, column=0, padx=5, sticky="w") + img_folder_file_path = tk.StringVar() + ttk.Entry(file_frame, textvariable=img_folder_file_path, width=40).grid(row=6, column=1, padx=5) + ttk.Button(file_frame, text="浏览", command=lambda: img_folder_file_path.set(filedialog.askdirectory())).grid(row=6, column=2, padx=5) + + # 调整处理文件按钮的行号 + + + + # 处理文件按钮 + file_button = ttk.Button(file_frame, text="执行处理") + file_button.grid(row=7, column=0, columnspan=5, pady=5) + + # 配置网格权重 + root.grid_rowconfigure(3, weight=1) + root.grid_columnconfigure(0, weight=1) + + # 按钮回调函数 + def process_folder_wrapper(): + folder = folder_path.get() + interim = interim_save.get() + final = final_save.get() + if not folder or not interim or not final: + messagebox.showerror("错误", "请填写所有路径") + return + + folder_button.config(state=tk.DISABLED) + def callback(e): + folder_button.config(state=tk.NORMAL) + if e: + messagebox.showerror("错误", str(e)) + else: + messagebox.showinfo("完成", "文件夹处理完成") + + thread = ProcessingThread( + func=process_floder, + args=(folder, interim, final, remove_dup.get(), del_time.get(), open_black.get(),re_recognize_var.get(), Path(img_folder_path.get()),matched_threshold_var.get(), ocr_threshold_var.get()), + callback=callback + ) + thread.start() + + def process_file_wrapper(): + input_file = file_path.get() + output_file = save_path.get() + if not input_file or not output_file: + messagebox.showerror("错误", "请填写所有路径") + return + + file_button.config(state=tk.DISABLED) + def callback(e): + file_button.config(state=tk.NORMAL) + if e: + messagebox.showerror("错误", str(e)) + else: + messagebox.showinfo("完成", "文件处理完成") + + thread = ProcessingThread( + func=process_file, + args=(input_file, output_file, remove_dup_file.get(), del_time_file.get(), open_black_file.get(),re_recognize_file_var.get(), Path(img_folder_file_path.get()),matched_threshold_file_var.get(), ocr_threshold_file_var.get()), + callback=callback + ) + thread.start() + + # 绑定按钮命令 + folder_button.config(command=process_folder_wrapper) + file_button.config(command=process_file_wrapper) + + return root + +if __name__ == "__main__": + # 请确保以下函数已经正确导入或定义: + # process_floder, process_file, find_csv_files, savecsv + + app = create_gui() + app.mainloop() diff --git a/src/tools/package.py b/tools/package.py similarity index 93% rename from src/tools/package.py rename to tools/package.py index a6d6fd2..1552a14 100644 --- a/src/tools/package.py +++ b/tools/package.py @@ -19,8 +19,8 @@ def _configure_utf8_stdio(): CONFIG = { "venv_dir": ".venv", # 虚拟环境目录 "source_script": "main.py", # 主程序文件路径 - "icon_file": r"src/resources/assets/icons/icon_64x64.ico", # 图标文件路径 - "output_dir": "build/dist", # 输出目录 + "icon_file": r"ico/icon_64x64.ico", # 图标文件路径 + "output_dir": "output", # 输出目录 "console": True, "add_data": [ # 需要打包的附加数据 (r".venv/Lib/site-packages/rapidocr/default_models.yaml", "rapidocr"), @@ -32,10 +32,15 @@ def _configure_utf8_stdio(): r"C:\Windows\System32\msvcp140.dll", r"C:\Windows\System32\vcruntime140.dll", r"C:\Windows\System32\vcruntime140_1.dll", - "vendor/bin/platform-tools", + # "arknights.csv", + # "models/best_model_full.onnx", + "images", + "platform-tools", + "ico", "pyproject.toml", - "vendor/bin/maafw", - "src", + "monster.csv", + "monster_greenvine.csv", + "maafw", ], } @@ -90,7 +95,7 @@ def copy_additional_files(): # 确保目标目录存在 dest.mkdir(parents=True, exist_ok=True) # 特殊处理images目录,排除tmp和nums子目录 - if src.name == "images" and "resources" in str(src): + if src.name == "images": def ignore_func(dir, names): """忽略tmp和nums子目录""" diff --git a/src/models/train.py b/train.py similarity index 70% rename from src/models/train.py rename to train.py index 42a5654..81549b0 100644 --- a/src/models/train.py +++ b/train.py @@ -2,7 +2,6 @@ from functools import cache from datetime import datetime from pathlib import Path -import sys import matplotlib.pyplot as plt import numpy as np @@ -12,22 +11,11 @@ import torch.optim as optim from sklearn.model_selection import train_test_split from torch.utils.data import DataLoader, Dataset +from config import FIELD_FEATURE_COUNT, MONSTER_COUNT -# Handle both direct execution and module import -if __name__ == "__main__": - # Direct execution: add parent to path - sys.path.insert(0, str(Path(__file__).parent.parent.parent)) - from src.core.config import FIELD_FEATURE_COUNT, MONSTER_COUNT - from src.models.model import UnitAwareTransformer - from src.models.muon import get_muon_lion_optimizers - from src.core.paths import PROJECT_ROOT -else: - # Module import - from ..core.config import FIELD_FEATURE_COUNT, MONSTER_COUNT - from .model import UnitAwareTransformer - from .muon import get_muon_lion_optimizers - from ..core.paths import PROJECT_ROOT - +# 导入拆分到 models 文件夹中的模型和 Muon 优化器方法 +from models.model import UnitAwareTransformer +from models.muon import get_muon_lion_optimizers print(f"场地特征数量: {FIELD_FEATURE_COUNT}") @@ -43,10 +31,7 @@ def get_device(prefer_gpu=True): if prefer_gpu: if torch.cuda.is_available(): return torch.device("cuda") - elif ( - hasattr(torch.backends, "mps") - and torch.backends.mps.is_available() - ): + elif hasattr(torch.backends, "mps") and torch.backends.mps.is_available(): return torch.device("mps") # Apple Silicon GPU elif hasattr(torch, "xpu") and torch.xpu.is_available(): # Intel GPU return torch.device("xpu") @@ -56,46 +41,7 @@ def get_device(prefer_gpu=True): device = get_device() -def resolve_data_file(csv_file: str) -> Path: - """解析训练数据路径,支持相对路径和常见回退路径。""" - requested = Path(csv_file) - - candidates = [] - # 优先:用户显式传入的路径(绝对或当前工作目录相对) - if requested.is_absolute(): - candidates.append(requested) - else: - candidates.append(requested) - # 再尝试以项目根目录为基准 - candidates.append(PROJECT_ROOT / requested) - - # 默认回退候选(当前仓库标准位置) - candidates.append(PROJECT_ROOT / "data" / "train" / "arknights.csv") - - for path in candidates: - if path.exists() and path.is_file(): - resolved = path.resolve() - if Path(csv_file) != resolved: - print(f"训练数据路径已解析为: {resolved}") - return resolved - - unique_candidates = [] - seen = set() - for path in candidates: - text = str(path) - if text not in seen: - seen.add(text) - unique_candidates.append(text) - - raise FileNotFoundError( - "未找到训练数据文件,请检查路径。已尝试以下位置:\n" - + "\n".join(f"- {p}" for p in unique_candidates) - ) - - -def plot_learning_curve( - train_losses, val_losses, train_accs, val_accs, save_path -): +def plot_learning_curve(train_losses, val_losses, train_accs, val_accs, save_path): """绘制学习曲线并保存为图片""" epochs = range(1, len(train_losses) + 1) @@ -103,21 +49,21 @@ def plot_learning_curve( # 绘制 Loss 曲线 plt.subplot(1, 2, 1) - plt.plot(epochs, train_losses, "b-", label="Train Loss") - plt.plot(epochs, val_losses, "r-", label="Val Loss") - plt.title("Training and Validation Loss") - plt.xlabel("Epochs") - plt.ylabel("Loss") + plt.plot(epochs, train_losses, 'b-', label='Train Loss') + plt.plot(epochs, val_losses, 'r-', label='Val Loss') + plt.title('Training and Validation Loss') + plt.xlabel('Epochs') + plt.ylabel('Loss') plt.legend() plt.grid(True) # 绘制 Accuracy 曲线 plt.subplot(1, 2, 2) - plt.plot(epochs, train_accs, "b-", label="Train Acc") - plt.plot(epochs, val_accs, "r-", label="Val Acc") - plt.title("Training and Validation Accuracy") - plt.xlabel("Epochs") - plt.ylabel("Accuracy (%)") + plt.plot(epochs, train_accs, 'b-', label='Train Acc') + plt.plot(epochs, val_accs, 'r-', label='Val Acc') + plt.title('Training and Validation Accuracy') + plt.xlabel('Epochs') + plt.ylabel('Accuracy (%)') plt.legend() plt.grid(True) @@ -129,27 +75,21 @@ def plot_learning_curve( def preprocess_data(csv_file): """预处理CSV文件,将异常值修正为合理范围""" - csv_path = resolve_data_file(csv_file) - print(f"预处理数据文件: {csv_path}") + print(f"预处理数据文件: {csv_file}") # 读取CSV文件 - data = pd.read_csv(csv_path, header=None, skiprows=1) + data = pd.read_csv(csv_file, header=None, skiprows=1) print(f"原始数据形状: {data.shape}") # 检查数据形状 expected_columns = TOTAL_FEATURE_COUNT + 2 # +2 for Result and ImgPath if data.shape[1] != expected_columns: + print(f"数据列数不符!期望 {expected_columns} 列,实际 {data.shape[1]} 列") print( - f"数据列数不符!期望 {expected_columns} 列,实际 {data.shape[1]} 列" - ) - print( - f"期望格式: {MONSTER_COUNT}(怪物L) + {FIELD_FEATURE_COUNT}(场地L) + {MONSTER_COUNT}(怪物R) + {FIELD_FEATURE_COUNT}(场地R) + 1(Result) + 1(ImgPath)" - ) + f"期望格式: {MONSTER_COUNT}(怪物L) + {FIELD_FEATURE_COUNT}(场地L) + {MONSTER_COUNT}(怪物R) + {FIELD_FEATURE_COUNT}(场地R) + 1(Result) + 1(ImgPath)") raise Exception("数据格式不符") - data = data.iloc[ - :, 0 : TOTAL_FEATURE_COUNT + 1 - ] # 保留特征和结果列,去掉ImgPath + data = data.iloc[:, 0: TOTAL_FEATURE_COUNT + 1] # 保留特征和结果列,去掉ImgPath # 检查特征范围 features = data.iloc[:, :-1] @@ -179,35 +119,23 @@ def preprocess_data(csv_file): class ArknightsDataset(Dataset): def __init__(self, csv_file, max_value=None): - csv_path = resolve_data_file(csv_file) - data = pd.read_csv(csv_path, header=None, skiprows=1) + data = pd.read_csv(csv_file, header=None, skiprows=1) # 检查数据形状 expected_columns = TOTAL_FEATURE_COUNT + 2 # +2 for Result and ImgPath if data.shape[1] != expected_columns: - print( - f"数据列数不符!期望 {expected_columns} 列,实际 {data.shape[1]} 列" - ) + print(f"数据列数不符!期望 {expected_columns} 列,实际 {data.shape[1]} 列") raise Exception("数据格式不符") - data = data.iloc[ - :, 0 : TOTAL_FEATURE_COUNT + 1 - ] # 保留特征和结果列,去掉ImgPath + data = data.iloc[:, 0: TOTAL_FEATURE_COUNT + 1] # 保留特征和结果列,去掉ImgPath features = data.iloc[:, :-1].values.astype(np.float32) labels = data.iloc[:, -1].map({"L": 0, "R": 1}).values - labels = np.where((labels != 0) & (labels != 1), 0, labels).astype( - np.float32 - ) + labels = np.where((labels != 0) & (labels != 1), 0, labels).astype(np.float32) # 分割双方单位和场地特征 # 数据格式: [怪物L(77), 场地L(6), 怪物R(77), 场地R(6)] left_monster_end = MONSTER_COUNT left_field_end = MONSTER_COUNT + FIELD_FEATURE_COUNT right_monster_end = MONSTER_COUNT + FIELD_FEATURE_COUNT + MONSTER_COUNT - right_field_end = ( - MONSTER_COUNT - + FIELD_FEATURE_COUNT - + MONSTER_COUNT - + FIELD_FEATURE_COUNT - ) + right_field_end = MONSTER_COUNT + FIELD_FEATURE_COUNT + MONSTER_COUNT + FIELD_FEATURE_COUNT # 提取各部分特征 left_monster_features = features[:, :left_monster_end] @@ -216,26 +144,10 @@ def __init__(self, csv_file, max_value=None): right_field_features = features[:, right_monster_end:right_field_end] # 合并怪物特征和场地特征(场地特征直接使用,不取绝对值和符号) - left_counts = np.concatenate( - [np.abs(left_monster_features), left_field_features], axis=1 - ) - right_counts = np.concatenate( - [np.abs(right_monster_features), right_field_features], axis=1 - ) - left_signs = np.concatenate( - [ - np.sign(left_monster_features), - np.ones_like(left_field_features), - ], - axis=1, - ) - right_signs = np.concatenate( - [ - np.sign(right_monster_features), - np.ones_like(right_field_features), - ], - axis=1, - ) + left_counts = np.concatenate([np.abs(left_monster_features), left_field_features], axis=1) + right_counts = np.concatenate([np.abs(right_monster_features), right_field_features], axis=1) + left_signs = np.concatenate([np.sign(left_monster_features), np.ones_like(left_field_features)], axis=1) + right_signs = np.concatenate([np.sign(right_monster_features), np.ones_like(right_field_features)], axis=1) if max_value is not None: left_counts = np.clip(left_counts, 0, max_value) @@ -261,9 +173,7 @@ def __getitem__(self, idx): ) -def train_one_epoch( - model, train_loader, criterion, muon_opt, lion_opt, scaler=None -): +def train_one_epoch(model, train_loader, criterion, muon_opt, lion_opt, scaler=None): model.train() total_loss = 0 correct = 0 @@ -280,19 +190,19 @@ def train_one_epoch( # 检查输入数据 if ( - torch.isnan(ls).any() - or torch.isnan(lc).any() - or torch.isnan(rs).any() - or torch.isnan(rc).any() + torch.isnan(ls).any() + or torch.isnan(lc).any() + or torch.isnan(rs).any() + or torch.isnan(rc).any() ): print("警告: 输入数据包含NaN,跳过该批次") continue if ( - torch.isinf(ls).any() - or torch.isinf(lc).any() - or torch.isinf(rs).any() - or torch.isinf(rc).any() + torch.isinf(ls).any() + or torch.isinf(lc).any() + or torch.isinf(rs).any() + or torch.isinf(rc).any() ): print("警告: 输入数据包含Inf,跳过该批次") continue @@ -304,7 +214,7 @@ def train_one_epoch( try: with torch.amp.autocast_mode.autocast( - device_type=device.type, enabled=(scaler is not None) + device_type=device.type, enabled=(scaler is not None) ): outputs = model(ls, lc, rs, rc).squeeze() # 确保输出在合理范围内 @@ -331,18 +241,14 @@ def train_one_epoch( scaler.unscale_(muon_opt) scaler.unscale_(lion_opt) # 梯度裁剪,避免梯度爆炸 - torch.nn.utils.clip_grad_norm_( - model.parameters(), max_norm=1.0 - ) + torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0) scaler.step(muon_opt) scaler.step(lion_opt) scaler.update() else: # 不使用混合精度 loss.backward() # 梯度裁剪,避免梯度爆炸 - torch.nn.utils.clip_grad_norm_( - model.parameters(), max_norm=1.0 - ) + torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0) muon_opt.step() lion_opt.step() @@ -355,9 +261,7 @@ def train_one_epoch( print(f"警告: 训练过程中出错 - {str(e)}") continue - return total_loss / max(1, len(train_loader)), 100 * correct / max( - 1, total - ) + return total_loss / max(1, len(train_loader)), 100 * correct / max(1, total) def evaluate(model, data_loader, criterion): @@ -369,20 +273,19 @@ def evaluate(model, data_loader, criterion): with torch.no_grad(): for ls, lc, rs, rc, labels in data_loader: ls, lc, rs, rc, labels = [ - x.to(device, non_blocking=True) - for x in (ls, lc, rs, rc, labels) + x.to(device, non_blocking=True) for x in (ls, lc, rs, rc, labels) ] # 检查输入值范围 if ( - torch.isnan(ls).any() - or torch.isnan(lc).any() - or torch.isnan(rs).any() - or torch.isnan(rc).any() - or torch.isinf(ls).any() - or torch.isinf(lc).any() - or torch.isinf(rs).any() - or torch.isinf(rc).any() + torch.isnan(ls).any() + or torch.isnan(lc).any() + or torch.isnan(rs).any() + or torch.isnan(rc).any() + or torch.isinf(ls).any() + or torch.isinf(lc).any() + or torch.isinf(rs).any() + or torch.isinf(rc).any() ): print("警告: 评估时输入数据包含NaN或Inf,跳过该批次") continue @@ -393,14 +296,11 @@ def evaluate(model, data_loader, criterion): try: with torch.amp.autocast_mode.autocast( - device_type=device.type, enabled=(device.type == "cuda") + device_type=device.type, enabled=(device.type == "cuda") ): outputs = model(ls, lc, rs, rc).squeeze() # 确保输出在合理范围内 - if ( - torch.isnan(outputs).any() - or torch.isinf(outputs).any() - ): + if torch.isnan(outputs).any() or torch.isinf(outputs).any(): print("警告: 评估时模型输出包含NaN或Inf,跳过该批次") continue # 确保输出严格在0-1之间 @@ -445,7 +345,7 @@ def stratified_random_split(dataset, test_size=0.1, seed=42): def main(): # 配置参数 config = { - "data_file": str(PROJECT_ROOT / "data" / "train" / "arknights.csv"), + "data_file": "arknights.csv", "batch_size": 1024, "test_size": 0.1, "embed_dim": 256, @@ -456,11 +356,9 @@ def main(): "lion_lr": 3e-4 / 10, # 论文指出 Lion 优化器需要更小的学习率 "epochs": 50, "seed": 42, # 随机数种子 - "save_dir": PROJECT_ROOT / "models", # 存到哪里 + "save_dir": "models", # 存到哪里 "max_feature_value": 100, # 限制特征最大值,防止极端值造成不稳定 - "num_workers": ( - 0 if torch.cuda.is_available() else 0 - ), # 根据CUDA可用性设置num_workers + "num_workers": 0 if torch.cuda.is_available() else 0, # 根据CUDA可用性设置num_workers } # 创建保存目录 @@ -524,9 +422,7 @@ def main(): num_workers=config["num_workers"], ) val_loader = DataLoader( - val_dataset, - batch_size=config["batch_size"], - num_workers=config["num_workers"], + val_dataset, batch_size=config["batch_size"], num_workers=config["num_workers"] ) # 初始化模型 @@ -540,27 +436,16 @@ def main(): dropout=config["dropout"], # 传入 dropout ).to(device) - print( - f"模型使用特征数: 怪物({MONSTER_COUNT}) + 场地({FIELD_FEATURE_COUNT}) = {total_units}" - ) - print( - f"模型参数数量: {sum(p.numel() for p in model.parameters() if p.requires_grad)}" - ) + print(f"模型使用特征数: 怪物({MONSTER_COUNT}) + 场地({FIELD_FEATURE_COUNT}) = {total_units}") + print(f"模型参数数量: {sum(p.numel() for p in model.parameters() if p.requires_grad)}") # 损失函数和优化器 (引入 Muon 与 Lion) criterion = nn.MSELoss() muon_opt, lion_opt = get_muon_lion_optimizers( - model, - muon_lr=config["lr"], - lion_lr=config["lion_lr"], - weight_decay=1e-1, - ) - scheduler_muon = optim.lr_scheduler.CosineAnnealingLR( - muon_opt, T_max=config["epochs"] - ) - scheduler_lion = optim.lr_scheduler.CosineAnnealingLR( - lion_opt, T_max=config["epochs"] + model, muon_lr=config["lr"], lion_lr=config["lion_lr"], weight_decay=1e-1 ) + scheduler_muon = optim.lr_scheduler.CosineAnnealingLR(muon_opt, T_max=config["epochs"]) + scheduler_lion = optim.lr_scheduler.CosineAnnealingLR(lion_opt, T_max=config["epochs"]) # 训练历史记录 train_losses, val_losses, train_accs, val_accs = [], [], [], [] @@ -616,9 +501,7 @@ def main(): # }, os.path.join(config['save_dir'], 'latest_checkpoint.pth')) # 打印训练信息 - print( - f"Train Loss: {train_loss:.4f} | Acc: {train_acc:.2f}% Val Loss: {val_loss:.4f} | Acc: {val_acc:.2f}%" - ) + print(f"Train Loss: {train_loss:.4f} | Acc: {train_acc:.2f}% Val Loss: {val_loss:.4f} | Acc: {val_acc:.2f}%") # 计时 if epoch == 0: @@ -630,9 +513,7 @@ def main(): elapsed_time = current_time - start_time avg_epoch_time = elapsed_time / (epoch + 1) remaining_time = (avg_epoch_time * config["epochs"]) - elapsed_time - print( - f"Epoch Time: {epoch_duration:.2f}s, Estimated Remaining: {remaining_time / 60:.2f}min" - ) + print(f"Epoch Time: {epoch_duration:.2f}s, Estimated Remaining: {remaining_time / 60:.2f}min") epoch_start_time = current_time # Reset for next epoch print("-" * 40) @@ -645,17 +526,10 @@ def main(): for model_type in ["acc", "loss", "full"]: old_path = save_dir_path / f"best_model_{model_type}.pth" if old_path.exists(): - old_path.rename( - save_dir_path / f"best_model_{model_type}_{base_filename}" - ) - - plot_learning_curve( - train_losses, - val_losses, - train_accs, - val_accs, - save_dir_path / f"learning_curve_{base_filename}.png", - ) + old_path.rename(save_dir_path / f"best_model_{model_type}_{base_filename}") + + plot_learning_curve(train_losses, val_losses, train_accs, val_accs, + save_dir_path / f"learning_curve_{base_filename}.png") if __name__ == "__main__": diff --git a/src/utils/unit.py b/unit.py similarity index 94% rename from src/utils/unit.py rename to unit.py index 8e2be97..d4c290c 100644 --- a/src/utils/unit.py +++ b/unit.py @@ -1,4 +1,4 @@ -from src.core.constants import UNIT_CONFIG +from constants import UNIT_CONFIG # 添加这行导入 class Unit: def __init__(self, team, unit_id, x, y): diff --git a/uv.lock b/uv.lock index ad093bc..6c882ef 100644 --- a/uv.lock +++ b/uv.lock @@ -2,68 +2,68 @@ version = 1 revision = 3 requires-python = ">=3.10" resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", @@ -94,12 +94,6 @@ resolution-markers = [ conflicts = [[ { package = "cannotmax-greenvine", extra = "cpu" }, { package = "cannotmax-greenvine", extra = "cu128" }, -], [ - { package = "cannotmax-greenvine", extra = "cpu" }, - { package = "cannotmax-greenvine", extra = "cu130" }, -], [ - { package = "cannotmax-greenvine", extra = "cu128" }, - { package = "cannotmax-greenvine", extra = "cu130" }, ]] [[package]] @@ -119,52 +113,45 @@ sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/3e/38/7859ff463 [[package]] name = "cannotmax-greenvine" -version = "2.0.0" +version = "1.0.8" source = { virtual = "." } dependencies = [ { name = "maafw" }, { name = "matplotlib" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "onnxruntime", version = "1.24.3", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "onnxruntime", version = "1.25.0", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "onnxruntime", version = "1.24.3", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "onnxruntime", version = "1.25.0", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, { name = "opencv-python" }, - { name = "pandas", version = "2.3.3", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "pandas", version = "3.0.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "pandas", version = "2.3.3", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "pandas", version = "3.0.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, { name = "pillow" }, { name = "pyqt6" }, { name = "pywin32" }, { name = "rapidocr" }, - { name = "scikit-learn", version = "1.7.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "scikit-learn", version = "1.8.0", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "scikit-learn", version = "1.7.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "scikit-learn", version = "1.8.0", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, { name = "toml" }, { name = "windows-capture" }, ] [package.optional-dependencies] cpu = [ - { name = "torch", version = "2.11.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "torch", version = "2.11.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "torchvision", version = "0.26.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "torchvision", version = "0.26.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "torch", version = "2.11.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(python_full_version < '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version >= '3.11' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "torch", version = "2.11.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(python_full_version < '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu')" }, + { name = "torchvision", version = "0.26.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(python_full_version < '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version >= '3.11' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "torchvision", version = "0.26.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(python_full_version < '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu')" }, ] cu128 = [ { name = "torch", version = "2.11.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" } }, { name = "torchvision", version = "0.26.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" } }, ] -cu130 = [ - { name = "torch", version = "2.11.0+cu130", source = { registry = "https://download.pytorch.org/whl/cu130" } }, - { name = "torchvision", version = "0.26.0+cu130", source = { registry = "https://download.pytorch.org/whl/cu130" } }, -] [package.dev-dependencies] dev = [ { name = "onnx" }, { name = "onnxscript" }, - { name = "pandas-stubs", version = "2.3.3.260113", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "pandas-stubs", version = "3.0.0.260204", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "pyinstaller" }, - { name = "types-toml" }, ] [package.metadata] @@ -183,21 +170,17 @@ requires-dist = [ { name = "toml" }, { name = "torch", marker = "extra == 'cpu'", index = "https://download.pytorch.org/whl/cpu", conflict = { package = "cannotmax-greenvine", extra = "cpu" } }, { name = "torch", marker = "extra == 'cu128'", specifier = ">=2.7.0", index = "https://download.pytorch.org/whl/cu128", conflict = { package = "cannotmax-greenvine", extra = "cu128" } }, - { name = "torch", marker = "extra == 'cu130'", specifier = ">=2.7.0", index = "https://download.pytorch.org/whl/cu130", conflict = { package = "cannotmax-greenvine", extra = "cu130" } }, { name = "torchvision", marker = "extra == 'cpu'", index = "https://download.pytorch.org/whl/cpu", conflict = { package = "cannotmax-greenvine", extra = "cpu" } }, { name = "torchvision", marker = "extra == 'cu128'", specifier = ">=0.22.0", index = "https://download.pytorch.org/whl/cu128", conflict = { package = "cannotmax-greenvine", extra = "cu128" } }, - { name = "torchvision", marker = "extra == 'cu130'", specifier = ">=0.22.0", index = "https://download.pytorch.org/whl/cu130", conflict = { package = "cannotmax-greenvine", extra = "cu130" } }, { name = "windows-capture" }, ] -provides-extras = ["cpu", "cu128", "cu130"] +provides-extras = ["cpu", "cu128"] [package.metadata.requires-dev] dev = [ { name = "onnx", specifier = ">=1.19.0" }, { name = "onnxscript", specifier = ">=0.7.0" }, - { name = "pandas-stubs" }, { name = "pyinstaller", specifier = ">=6.15.0" }, - { name = "types-toml" }, ] [[package]] @@ -328,7 +311,7 @@ name = "colorlog" version = "6.10.1" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "colorama", marker = "sys_platform == 'win32' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/a2/61/f083b5ac52e505dfc1c624eafbf8c7589a0d7f32daa398d2e7590efa5fda/colorlog-6.10.1.tar.gz", hash = "sha256:eb4ae5cb65fe7fec7773c2306061a8e63e02efc2c72eba9d27b0fa23c94f1321" } wheels = [ @@ -340,17 +323,17 @@ name = "contourpy" version = "1.3.2" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54" } wheels = [ @@ -417,62 +400,62 @@ name = "contourpy" version = "1.3.3" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", @@ -499,7 +482,7 @@ resolution-markers = [ "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880" } wheels = [ @@ -580,18 +563,6 @@ wheels = [ name = "cuda-bindings" version = "12.9.6" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version < '3.11' and platform_machine != 's390x'", - "python_full_version < '3.11' and platform_machine == 's390x'", -] dependencies = [ { name = "cuda-pathfinder", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, ] @@ -619,46 +590,6 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/4b/98/8e5363d00c959d4172b1d619a4f03af454bf9952636224f0ac0f5c35c067/cuda_bindings-12.9.6-cp314-cp314t-win_amd64.whl", hash = "sha256:7f0a08eba6e807d041bf6f2ba66d84db1ddf54787399dfac716497ef40fb5fc3" }, ] -[[package]] -name = "cuda-bindings" -version = "13.2.0" -source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version < '3.11' and platform_machine != 's390x'", - "python_full_version < '3.11' and platform_machine == 's390x'", -] -dependencies = [ - { name = "cuda-pathfinder", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, -] -wheels = [ - { url = "https://mirrors.cloud.tencent.com/pypi/packages/1a/fe/7351d7e586a8b4c9f89731bfe4cf0148223e8f9903ff09571f78b3fb0682/cuda_bindings-13.2.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08b395f79cb89ce0cd8effff07c4a1e20101b873c256a1aeb286e8fd7bd0f556" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/aa/ef/184aa775e970fc089942cd9ec6302e6e44679d4c14549c6a7ea45bf7f798/cuda_bindings-13.2.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6f3682ec3c4769326aafc67c2ba669d97d688d0b7e63e659d36d2f8b72f32d6" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/ec/ea/81999d01375645f34596c76eb046b4b36d58cc6fe2bddb2410f8a7b7a827/cuda_bindings-13.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:845025438a1b9e20718b9fb42add3e0eb72e85458bcab3eeb80bfd8f0a9dab33" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/e0/a9/3a8241c6e19483ac1f1dcf5c10238205dcb8a6e9d0d4d4709240dff28ff4/cuda_bindings-13.2.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:721104c603f059780d287969be3d194a18d0cc3b713ed9049065a1107706759d" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/e9/94/2748597f47bb1600cd466b20cab4159f1530a3a33fe7f70fee199b3abb9e/cuda_bindings-13.2.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1eba9504ac70667dd48313395fe05157518fd6371b532790e96fbb31bbb5a5e1" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/29/5a/0ce1731c48bcd9f40996a4ef1abbf634f1a7fe4a15c5050b1e75ce3a7acf/cuda_bindings-13.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:debb51b211d246f8326f6b6e982506a5d0d9906672c91bc478b66addc7ecc60a" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/52/c8/b2589d68acf7e3d63e2be330b84bc25712e97ed799affbca7edd7eae25d6/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e865447abfb83d6a98ad5130ed3c70b1fc295ae3eeee39fd07b4ddb0671b6788" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/1f/92/f899f7bbb5617bb65ec52a6eac1e9a1447a86b916c4194f8a5001b8cde0c/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46d8776a55d6d5da9dd6e9858fba2efcda2abe6743871dee47dd06eb8cb6d955" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/bb/a5/d7f01a415e134546248cef612adad8153c9f1eb10ec79505a7cd8294370b/cuda_bindings-13.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:45815daeb595bf3b405c52671a2542b1f8e9329f3b029494acbfcc74aeaa1f2d" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/df/93/eef988860a3ca985f82c4f3174fc0cdd94e07331ba9a92e8e064c260337f/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6629ca2df6f795b784752409bcaedbd22a7a651b74b56a165ebc0c9dcbd504d0" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/18/23/6db3aba46864aee357ab2415135b3fe3da7e9f1fa0221fa2a86a5968099c/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7dca0da053d3b4cc4869eff49c61c03f3c5dbaa0bcd712317a358d5b8f3f385d" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/c4/84/d3b6220b51cbc02ca14db7387e97445126b4ff5125aaa6c5dd7dcb75e679/cuda_bindings-13.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:8cebe3ce4aeeca5af9c490e175f76c4b569bbf4a35a62294b777bc77bf7ac4d8" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/c0/87/87a014f045b77c6de5c8527b0757fe644417b184e5367db977236a141602/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6464b30f46692d6c7f65d4a0e0450d81dd29de3afc1bb515653973d01c2cd6e" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/ee/5e/c0fe77a73aaefd3fff25ffaccaac69c5a63eafdf8b9a4c476626ef0ac703/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4af9f3e1be603fa12d5ad6cfca7844c9d230befa9792b5abdf7dd79979c3626" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/e3/73/98bcb069778fe420226db75aff54b5dd6c3ecfd0912edabab723326e80b7/cuda_bindings-13.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:bd658bb5c0e55b7b3e5dd0ed509c6addb298c665db26a9bfba35e1e626000ba2" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/5f/58/ed2c3b39c8dd5f96aa7a4abef0d47a73932c7a988e30f5fa428f00ed0da1/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df850a1ff8ce1b3385257b08e47b70e959932f5f432d0a4e46a355962b4e4771" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/1f/01/0c941b112ceeb21439b05895eace78ca1aa2eaaf695c8521a068fd9b4c00/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8a16384c6494e5485f39314b0b4afb04bee48d49edb16d5d8593fd35bbd231b" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/52/49/4e01cc06447d39476e138d1b1adec8d35c0d04eccd2c8d69befc08cd66e8/cuda_bindings-13.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6ccf14e0c1def3b7200100aafff3a9f7e210ecb6e409329e92dcf6cd2c00d5c7" }, -] - [[package]] name = "cuda-pathfinder" version = "1.5.3" @@ -671,18 +602,6 @@ wheels = [ name = "cuda-toolkit" version = "12.8.1" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version < '3.11' and platform_machine != 's390x'", - "python_full_version < '3.11' and platform_machine == 's390x'", -] wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/d4/c8/7dce3a0b15b42a3b58e7d96eb22a687d3bf2c44e01d149a6874629cd9938/cuda_toolkit-12.8.1-py2.py3-none-any.whl", hash = "sha256:adc7906af4ecbf9a352f9dca5734eceb21daec281ccfcf5675e1d2f724fc2cba" }, ] @@ -722,61 +641,6 @@ nvtx = [ { name = "nvidia-nvtx-cu12", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, ] -[[package]] -name = "cuda-toolkit" -version = "13.0.2" -source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version < '3.11' and platform_machine != 's390x'", - "python_full_version < '3.11' and platform_machine == 's390x'", -] -wheels = [ - { url = "https://mirrors.cloud.tencent.com/pypi/packages/57/b2/453099f5f3b698d7d0eab38916aac44c7f76229f451709e2eb9db6615dcd/cuda_toolkit-13.0.2-py2.py3-none-any.whl", hash = "sha256:b198824cf2f54003f50d64ada3a0f184b42ca0846c1c94192fa269ecd97a66eb" }, -] - -[package.optional-dependencies] -cublas = [ - { name = "nvidia-cublas", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, -] -cudart = [ - { name = "nvidia-cuda-runtime", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, -] -cufft = [ - { name = "nvidia-cufft", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, -] -cufile = [ - { name = "nvidia-cufile", marker = "sys_platform == 'linux'" }, -] -cupti = [ - { name = "nvidia-cuda-cupti", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, -] -curand = [ - { name = "nvidia-curand", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, -] -cusolver = [ - { name = "nvidia-cusolver", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, -] -cusparse = [ - { name = "nvidia-cusparse", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, -] -nvjitlink = [ - { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, -] -nvrtc = [ - { name = "nvidia-cuda-nvrtc", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, -] -nvtx = [ - { name = "nvidia-nvtx", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, -] - [[package]] name = "cycler" version = "0.12.1" @@ -1037,8 +901,8 @@ version = "5.10.2" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ { name = "maaagentbinary" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, { name = "strenum" }, ] wheels = [ @@ -1055,7 +919,7 @@ name = "macholib" version = "1.16.4" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ - { name = "altgraph", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu') or sys_platform == 'darwin' or (sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu') or (sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "altgraph", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu') or sys_platform == 'darwin' or (sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu') or (sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/10/2f/97589876ea967487978071c9042518d28b958d87b17dceb7cdc1d881f963/macholib-1.16.4.tar.gz", hash = "sha256:f408c93ab2e995cd2c46e34fe328b130404be143469e41bc366c807448979362" } wheels = [ @@ -1152,13 +1016,13 @@ name = "matplotlib" version = "3.10.9" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ - { name = "contourpy", version = "1.3.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "contourpy", version = "1.3.3", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "contourpy", version = "1.3.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "contourpy", version = "1.3.3", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, { name = "cycler" }, { name = "fonttools" }, { name = "kiwisolver" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, { name = "packaging" }, { name = "pillow" }, { name = "pyparsing" }, @@ -1227,8 +1091,8 @@ name = "ml-dtypes" version = "0.5.4" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/0e/4a/c27b42ed9b1c7d13d9ba8b6905dece787d6259152f2309338aed29b2447b/ml_dtypes-0.5.4.tar.gz", hash = "sha256:8ab06a50fb9bf9666dd0fe5dfb4676fa2b0ac0f31ecff72a6c3af8e22c063453" } wheels = [ @@ -1282,14 +1146,12 @@ name = "networkx" version = "3.4.2" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1" } wheels = [ @@ -1301,86 +1163,62 @@ name = "networkx" version = "3.6.1" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509" } wheels = [ @@ -1392,12 +1230,12 @@ name = "numpy" version = "2.2.6" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] @@ -1464,62 +1302,62 @@ name = "numpy" version = "2.4.4" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", @@ -1620,16 +1458,6 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/04/74/f4c001f4714c3ad9ce037e18cf2b9c64871a84951eaa0baf683a9ca9301c/numpy-2.4.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f2cf083b324a467e1ab358c105f6cad5ea950f50524668a80c486ff1db24e119" }, ] -[[package]] -name = "nvidia-cublas" -version = "13.1.0.3" -source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } -wheels = [ - { url = "https://mirrors.cloud.tencent.com/pypi/packages/e1/a5/fce49e2ae977e0ccc084e5adafceb4f0ac0c8333cb6863501618a7277f67/nvidia_cublas-13.1.0.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:c86fc7f7ae36d7528288c5d88098edcb7b02c633d262e7ddbb86b0ad91be5df2" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/e7/44/423ac00af4dd95a5aeb27207e2c0d9b7118702149bf4704c3ddb55bb7429/nvidia_cublas-13.1.0.3-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:ee8722c1f0145ab246bccb9e452153b5e0515fd094c3678df50b2a0888b8b171" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/10/f5/f50bc3f5c2bb57ab8f5b4d78bc1146b57810d42cb8fcb28cbe2e14050376/nvidia_cublas-13.1.0.3-py3-none-win_amd64.whl", hash = "sha256:2a3b94a37def342471c59fad7856caee4926809a72dd5270155d6a31b5b277be" }, -] - [[package]] name = "nvidia-cublas-cu12" version = "12.8.4.1" @@ -1640,16 +1468,6 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/70/61/7d7b3c70186fb651d0fbd35b01dbfc8e755f69fd58f817f3d0f642df20c3/nvidia_cublas_cu12-12.8.4.1-py3-none-win_amd64.whl", hash = "sha256:47e9b82132fa8d2b4944e708049229601448aaad7e6f296f630f2d1a32de35af" }, ] -[[package]] -name = "nvidia-cuda-cupti" -version = "13.0.85" -source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } -wheels = [ - { url = "https://mirrors.cloud.tencent.com/pypi/packages/2a/2a/80353b103fc20ce05ef51e928daed4b6015db4aaa9162ed0997090fe2250/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_aarch64.whl", hash = "sha256:796bd679890ee55fb14a94629b698b6db54bcfd833d391d5e94017dd9d7d3151" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/33/6d/737d164b4837a9bbd202f5ae3078975f0525a55730fe871d8ed4e3b952b0/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_x86_64.whl", hash = "sha256:4eb01c08e859bf924d222250d2e8f8b8ff6d3db4721288cf35d14252a4d933c8" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/ad/df/b74b10025c1205695c5676373f2edd3e87a7202cc62ead0dfbc373b0f6ea/nvidia_cuda_cupti-13.0.85-py3-none-win_amd64.whl", hash = "sha256:683f58d301548deeefcb8f6fac1b8d907691b9d8b18eccab417f51e362102f00" }, -] - [[package]] name = "nvidia-cuda-cupti-cu12" version = "12.8.90" @@ -1660,16 +1478,6 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/41/bc/83f5426095d93694ae39fe1311431b5d5a9bb82e48bf0dd8e19be2765942/nvidia_cuda_cupti_cu12-12.8.90-py3-none-win_amd64.whl", hash = "sha256:bb479dcdf7e6d4f8b0b01b115260399bf34154a1a2e9fe11c85c517d87efd98e" }, ] -[[package]] -name = "nvidia-cuda-nvrtc" -version = "13.0.88" -source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } -wheels = [ - { url = "https://mirrors.cloud.tencent.com/pypi/packages/c3/68/483a78f5e8f31b08fb1bb671559968c0ca3a065ac7acabfc7cee55214fd6/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:ad9b6d2ead2435f11cbb6868809d2adeeee302e9bb94bcf0539c7a40d80e8575" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/b7/dc/6bb80850e0b7edd6588d560758f17e0550893a1feaf436807d64d2da040f/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d27f20a0ca67a4bb34268a5e951033496c5b74870b868bacd046b1b8e0c3267b" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/4a/af/345fedb9f4c76c84ab4fa445b36bd4048a4d9db60e6bc76b4f913ff4b852/nvidia_cuda_nvrtc-13.0.88-py3-none-win_amd64.whl", hash = "sha256:6bcd4e7f8e205cbe644f5a98f2f799bef9556fefc89dd786e79a16312ce49872" }, -] - [[package]] name = "nvidia-cuda-nvrtc-cu12" version = "12.8.93" @@ -1680,16 +1488,6 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/45/51/52a3d84baa2136cc8df15500ad731d74d3a1114d4c123e043cb608d4a32b/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-win_amd64.whl", hash = "sha256:7a4b6b2904850fe78e0bd179c4b655c404d4bb799ef03ddc60804247099ae909" }, ] -[[package]] -name = "nvidia-cuda-runtime" -version = "13.0.96" -source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } -wheels = [ - { url = "https://mirrors.cloud.tencent.com/pypi/packages/87/4f/17d7b9b8e285199c58ce28e31b5c5bbaa4d8271af06a89b6405258245de2/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ef9bcbe90493a2b9d810e43d249adb3d02e98dd30200d86607d8d02687c43f55" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/2e/24/d1558f3b68b1d26e706813b1d10aa1d785e4698c425af8db8edc3dced472/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f82250d7782aa23b6cfe765ecc7db554bd3c2870c43f3d1821f1d18aebf0548" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/b7/94/6b867483bec07da24ffa32736c79fabb94ef3a7af4d787a9d4a974868576/nvidia_cuda_runtime-13.0.96-py3-none-win_amd64.whl", hash = "sha256:f79298c8a098cec150a597c8eba58ecdab96e3bdc4b9bc4f9983635031740492" }, -] - [[package]] name = "nvidia-cuda-runtime-cu12" version = "12.8.90" @@ -1713,32 +1511,6 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/a7/a5/48f07449fc9c6cc146dcafe6149fa5d69630137d2ec5b7d9e09f255fadd7/nvidia_cudnn_cu12-9.19.0.56-py3-none-win_amd64.whl", hash = "sha256:cec70596b9ce878fab83810c3f5a2e606d35f510e5fee579759e4cbc68a23750" }, ] -[[package]] -name = "nvidia-cudnn-cu13" -version = "9.19.0.56" -source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } -dependencies = [ - { name = "nvidia-cublas", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, -] -wheels = [ - { url = "https://mirrors.cloud.tencent.com/pypi/packages/f1/84/26025437c1e6b61a707442184fa0c03d083b661adf3a3eecfd6d21677740/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:6ed29ffaee1176c612daf442e4dd6cfeb6a0caa43ddcbeb59da94953030b1be4" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/a3/22/0b4b932655d17a6da1b92fa92ab12844b053bb2ac2475e179ba6f043da1e/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:d20e1734305e9d68889a96e3f35094d733ff1f83932ebe462753973e53a572bf" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/91/a2/f020386683ee9ab2c9a9f7f79290d9b0d07f7241de54dc746af2abd188d2/nvidia_cudnn_cu13-9.19.0.56-py3-none-win_amd64.whl", hash = "sha256:40d8c375005bcb01495f8edf375230b203a411a0c05fb6dc92a3781edcb23eac" }, -] - -[[package]] -name = "nvidia-cufft" -version = "12.0.0.61" -source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } -dependencies = [ - { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, -] -wheels = [ - { url = "https://mirrors.cloud.tencent.com/pypi/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/a8/2f/7b57e29836ea8714f81e9898409196f47d772d5ddedddf1592eadb8ab743/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6c44f692dce8fd5ffd3e3df134b6cdb9c2f72d99cf40b62c32dde45eea9ddad3" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/85/b2/f8af21a2ed1beed337a6a02c5a28aeb85441f4d578ec3d529543c775ea4b/nvidia_cufft-12.0.0.61-py3-none-win_amd64.whl", hash = "sha256:2abce5b39d2f5ae12730fb7e5db6696533e36c26e2d3e8fd1750bdd2853364eb" }, -] - [[package]] name = "nvidia-cufft-cu12" version = "11.3.3.83" @@ -1752,15 +1524,6 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/7d/ec/ce1629f1e478bb5ccd208986b5f9e0316a78538dd6ab1d0484f012f8e2a1/nvidia_cufft_cu12-11.3.3.83-py3-none-win_amd64.whl", hash = "sha256:7a64a98ef2a7c47f905aaf8931b69a3a43f27c55530c698bb2ed7c75c0b42cb7" }, ] -[[package]] -name = "nvidia-cufile" -version = "1.15.1.6" -source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } -wheels = [ - { url = "https://mirrors.cloud.tencent.com/pypi/packages/3f/70/4f193de89a48b71714e74602ee14d04e4019ad36a5a9f20c425776e72cd6/nvidia_cufile-1.15.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08a3ecefae5a01c7f5117351c64f17c7c62efa5fffdbe24fc7d298da19cd0b44" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/ab/73/cc4a14c9813a8a0d509417cf5f4bdaba76e924d58beb9864f5a7baceefbf/nvidia_cufile-1.15.1.6-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:bdc0deedc61f548bddf7733bdc216456c2fdb101d020e1ab4b88d232d5e2f6d1" }, -] - [[package]] name = "nvidia-cufile-cu12" version = "1.13.1.3" @@ -1770,16 +1533,6 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/1e/f5/5607710447a6fe9fd9b3283956fceeee8a06cda1d2f56ce31371f595db2a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:4beb6d4cce47c1a0f1013d72e02b0994730359e17801d395bdcbf20cfb3bb00a" }, ] -[[package]] -name = "nvidia-curand" -version = "10.4.0.35" -source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } -wheels = [ - { url = "https://mirrors.cloud.tencent.com/pypi/packages/1e/72/7c2ae24fb6b63a32e6ae5d241cc65263ea18d08802aaae087d9f013335a2/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:133df5a7509c3e292aaa2b477afd0194f06ce4ea24d714d616ff36439cee349a" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/a5/9f/be0a41ca4a4917abf5cb9ae0daff1a6060cc5de950aec0396de9f3b52bc5/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:1aee33a5da6e1db083fe2b90082def8915f30f3248d5896bcec36a579d941bfc" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/99/27/72103153b1ffc00e09fdc40ac970235343dcd1ea8bd762e84d2d73219ffa/nvidia_curand-10.4.0.35-py3-none-win_amd64.whl", hash = "sha256:65b1710aa6961d326b411e314b374290904c5ddf41dc3f766ebc3f1d7d4ca69f" }, -] - [[package]] name = "nvidia-curand-cu12" version = "10.3.9.90" @@ -1790,21 +1543,6 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/b9/75/70c05b2f3ed5be3bb30b7102b6eb78e100da4bbf6944fd6725c012831cab/nvidia_curand_cu12-10.3.9.90-py3-none-win_amd64.whl", hash = "sha256:f149a8ca457277da854f89cf282d6ef43176861926c7ac85b2a0fbd237c587ec" }, ] -[[package]] -name = "nvidia-cusolver" -version = "12.0.4.66" -source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } -dependencies = [ - { name = "nvidia-cublas", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, - { name = "nvidia-cusparse", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, - { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, -] -wheels = [ - { url = "https://mirrors.cloud.tencent.com/pypi/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/5f/67/cba3777620cdacb99102da4042883709c41c709f4b6323c10781a9c3aa34/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0a759da5dea5c0ea10fd307de75cdeb59e7ea4fcb8add0924859b944babf1112" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/99/ef/332a0101260ca78a1daef046bf0b06199e8ed4dac1d2aa698289c358169c/nvidia_cusolver-12.0.4.66-py3-none-win_amd64.whl", hash = "sha256:16515bd33a8e76bb54d024cfa068fa68d30e80fc34b9e1090813ea9362e0cb65" }, -] - [[package]] name = "nvidia-cusolver-cu12" version = "11.7.3.90" @@ -1820,19 +1558,6 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/13/c0/76ca8551b8a84146ffa189fec81c26d04adba4bc0dbe09cd6e6fd9b7de04/nvidia_cusolver_cu12-11.7.3.90-py3-none-win_amd64.whl", hash = "sha256:4a550db115fcabc4d495eb7d39ac8b58d4ab5d8e63274d3754df1c0ad6a22d34" }, ] -[[package]] -name = "nvidia-cusparse" -version = "12.6.3.3" -source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } -dependencies = [ - { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, -] -wheels = [ - { url = "https://mirrors.cloud.tencent.com/pypi/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/fa/18/623c77619c31d62efd55302939756966f3ecc8d724a14dab2b75f1508850/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b3c89c88d01ee0e477cb7f82ef60a11a4bcd57b6b87c33f789350b59759360b" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/02/b0/b043d6f3480f102f885cf87fc3ffd3edcb5e23b855025a50e2ef4d059185/nvidia_cusparse-12.6.3.3-py3-none-win_amd64.whl", hash = "sha256:cbcf42feb737bd7ec15b4c0a63e62351886bd3f975027b8815d7f720a2b5ea79" }, -] - [[package]] name = "nvidia-cusparse-cu12" version = "12.5.8.93" @@ -1856,16 +1581,6 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/2f/d8/a6b0d0d0c2435e9310f3e2bb0d9c9dd4c33daef86aa5f30b3681defd37ea/nvidia_cusparselt_cu12-0.7.1-py3-none-win_amd64.whl", hash = "sha256:f67fbb5831940ec829c9117b7f33807db9f9678dc2a617fbe781cac17b4e1075" }, ] -[[package]] -name = "nvidia-cusparselt-cu13" -version = "0.8.0" -source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } -wheels = [ - { url = "https://mirrors.cloud.tencent.com/pypi/packages/46/10/8dcd1175260706a2fc92a16a52e306b71d4c1ea0b0cc4a9484183399818a/nvidia_cusparselt_cu13-0.8.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:400c6ed1cf6780fc6efedd64ec9f1345871767e6a1a0a552a1ea0578117ea77c" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/fd/53/43b0d71f4e702fa9733f8b4571fdca50a8813f1e450b656c239beff12315/nvidia_cusparselt_cu13-0.8.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:25e30a8a7323935d4ad0340b95a0b69926eee755767e8e0b1cf8dd85b197d3fd" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/57/de/8f0578928b9b1246d7b1324db0528e6b9f9fb54496a49f40bf71f09f1a27/nvidia_cusparselt_cu13-0.8.0-py3-none-win_amd64.whl", hash = "sha256:e80212ed7b1afc97102fbb2b5c82487aa73f6a0edfa6d26c5a152593e520bb8f" }, -] - [[package]] name = "nvidia-nccl-cu12" version = "2.28.9" @@ -1875,25 +1590,6 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/4a/4e/44dbb46b3d1b0ec61afda8e84837870f2f9ace33c564317d59b70bc19d3e/nvidia_nccl_cu12-2.28.9-py3-none-manylinux_2_18_x86_64.whl", hash = "sha256:485776daa8447da5da39681af455aa3b2c2586ddcf4af8772495e7c532c7e5ab" }, ] -[[package]] -name = "nvidia-nccl-cu13" -version = "2.28.9" -source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } -wheels = [ - { url = "https://mirrors.cloud.tencent.com/pypi/packages/39/55/1920646a2e43ffd4fc958536b276197ed740e9e0c54105b4bb3521591fc7/nvidia_nccl_cu13-2.28.9-py3-none-manylinux_2_18_aarch64.whl", hash = "sha256:01c873ba1626b54caa12272ed228dc5b2781545e0ae8ba3f432a8ef1c6d78643" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/b0/b4/878fefaad5b2bcc6fcf8d474a25e3e3774bc5133e4b58adff4d0bca238bc/nvidia_nccl_cu13-2.28.9-py3-none-manylinux_2_18_x86_64.whl", hash = "sha256:e4553a30f34195f3fa1da02a6da3d6337d28f2003943aa0a3d247bbc25fefc42" }, -] - -[[package]] -name = "nvidia-nvjitlink" -version = "13.0.88" -source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } -wheels = [ - { url = "https://mirrors.cloud.tencent.com/pypi/packages/56/7a/123e033aaff487c77107195fa5a2b8686795ca537935a24efae476c41f05/nvidia_nvjitlink-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:13a74f429e23b921c1109976abefacc69835f2f433ebd323d3946e11d804e47b" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/ab/2c/93c5250e64df4f894f1cbb397c6fd71f79813f9fd79d7cd61de3f97b3c2d/nvidia_nvjitlink-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e931536ccc7d467a98ba1d8b89ff7fa7f1fa3b13f2b0069118cd7f47bff07d0c" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/e4/01/07530b0e37546231052e30234540289c42eaffa486f1a34a87fed340157b/nvidia_nvjitlink-13.0.88-py3-none-win_amd64.whl", hash = "sha256:634e96e3da9ef845ae744097a1f289238ecf946ce0b82e93cdce14b9782e682f" }, -] - [[package]] name = "nvidia-nvjitlink-cu12" version = "12.8.93" @@ -1913,25 +1609,6 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/b5/09/6ea3ea725f82e1e76684f0708bbedd871fc96da89945adeba65c3835a64c/nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:042f2500f24c021db8a06c5eec2539027d57460e1c1a762055a6554f72c369bd" }, ] -[[package]] -name = "nvidia-nvshmem-cu13" -version = "3.4.5" -source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } -wheels = [ - { url = "https://mirrors.cloud.tencent.com/pypi/packages/dc/0f/05cc9c720236dcd2db9c1ab97fff629e96821be2e63103569da0c9b72f19/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dc2a197f38e5d0376ad52cd1a2a3617d3cdc150fd5966f4aee9bcebb1d68fe9" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/3c/35/a9bf80a609e74e3b000fef598933235c908fcefcef9026042b8e6dfde2a9/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:290f0a2ee94c9f3687a02502f3b9299a9f9fe826e6d0287ee18482e78d495b80" }, -] - -[[package]] -name = "nvidia-nvtx" -version = "13.0.85" -source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } -wheels = [ - { url = "https://mirrors.cloud.tencent.com/pypi/packages/c2/f3/d86c845465a2723ad7e1e5c36dcd75ddb82898b3f53be47ebd429fb2fa5d/nvidia_nvtx-13.0.85-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4936d1d6780fbe68db454f5e72a42ff64d1fd6397df9f363ae786930fd5c1cd4" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/a8/64/3708a90d1ebe202ffdeb7185f878a3c84d15c2b2c31858da2ce0583e2def/nvidia_nvtx-13.0.85-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb7780edb6b14107373c835bf8b72e7a178bac7367e23da7acb108f973f157a6" }, - { url = "https://mirrors.cloud.tencent.com/pypi/packages/d2/50/0e2220f8620a177de994211186ffc5bfa9f2ce1e1282797f8f90096f9f88/nvidia_nvtx-13.0.85-py3-none-win_amd64.whl", hash = "sha256:d66ea44254dd3c6eacc300047af6e1288d2269dd072b417e0adffbf479e18519" }, -] - [[package]] name = "nvidia-nvtx-cu12" version = "12.8.90" @@ -1961,8 +1638,8 @@ version = "1.21.0" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ { name = "ml-dtypes" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, { name = "protobuf" }, { name = "typing-extensions" }, ] @@ -2003,8 +1680,8 @@ version = "0.2.1" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ { name = "ml-dtypes" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, { name = "onnx" }, { name = "sympy" }, { name = "typing-extensions" }, @@ -2019,21 +1696,21 @@ name = "onnxruntime" version = "1.24.3" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "flatbuffers", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "packaging", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "protobuf", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "sympy", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "flatbuffers", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "packaging", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "protobuf", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "sympy", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, ] wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/15/41/3253db975a90c3ce1d475e2a230773a21cd7998537f0657947df6fb79861/onnxruntime-1.24.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3e6456801c66b095c5cd68e690ca25db970ea5202bd0c5b84a2c3ef7731c5a3c" }, @@ -2067,62 +1744,62 @@ name = "onnxruntime" version = "1.25.0" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", @@ -2149,10 +1826,10 @@ resolution-markers = [ "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "flatbuffers", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "packaging", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "protobuf", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "flatbuffers", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "packaging", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "protobuf", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, ] wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/66/67/edb2cb6c4a38ebf539c3529c5449d68b8031f3006d8ee36574a6d45559b2/onnxruntime-1.25.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a71baa8e0e2f3417106e3a8b2183fd5741875b998041f1a2422a1d0240f302cb" }, @@ -2187,8 +1864,8 @@ version = "0.7.0" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ { name = "ml-dtypes" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, { name = "onnx" }, { name = "onnx-ir" }, { name = "packaging" }, @@ -2204,8 +1881,8 @@ name = "opencv-python" version = "4.13.0.92" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, ] wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/fc/6f/5a28fef4c4a382be06afe3938c64cc168223016fa520c5abaf37e8862aa5/opencv_python-4.13.0.92-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:caf60c071ec391ba51ed00a4a920f996d0b64e3e46068aac1f646b5de0326a19" }, @@ -2232,20 +1909,20 @@ name = "pandas" version = "2.3.3" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "python-dateutil", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "pytz", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "tzdata", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "python-dateutil", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "pytz", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "tzdata", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b" } wheels = [ @@ -2303,62 +1980,62 @@ name = "pandas" version = "3.0.2" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", @@ -2385,9 +2062,9 @@ resolution-markers = [ "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "python-dateutil", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "tzdata", marker = "(python_full_version >= '3.11' and sys_platform == 'emscripten') or (python_full_version >= '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "python-dateutil", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "tzdata", marker = "(python_full_version >= '3.11' and sys_platform == 'emscripten') or (python_full_version >= '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/da/99/b342345300f13440fe9fe385c3c481e2d9a595ee3bab4d3219247ac94e9a/pandas-3.0.2.tar.gz", hash = "sha256:f4753e73e34c8d83221ba58f232433fca2748be8b18dbca02d242ed153945043" } wheels = [ @@ -2440,123 +2117,6 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/cb/2b/f8434233fab2bd66a02ec014febe4e5adced20e2693e0e90a07d118ed30e/pandas-3.0.2-cp314-cp314t-win_arm64.whl", hash = "sha256:5371b72c2d4d415d08765f32d689217a43227484e81b2305b52076e328f6f482" }, ] -[[package]] -name = "pandas-stubs" -version = "2.3.3.260113" -source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } -resolution-markers = [ - "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", -] -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "types-pytz", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, -] -sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/92/5d/be23854a73fda69f1dbdda7bc10fbd6f930bd1fa87aaec389f00c901c1e8/pandas_stubs-2.3.3.260113.tar.gz", hash = "sha256:076e3724bcaa73de78932b012ec64b3010463d377fa63116f4e6850643d93800" } -wheels = [ - { url = "https://mirrors.cloud.tencent.com/pypi/packages/d1/c6/df1fe324248424f77b89371116dab5243db7f052c32cc9fe7442ad9c5f75/pandas_stubs-2.3.3.260113-py3-none-any.whl", hash = "sha256:ec070b5c576e1badf12544ae50385872f0631fc35d99d00dc598c2954ec564d3" }, -] - -[[package]] -name = "pandas-stubs" -version = "3.0.0.260204" -source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", -] -dependencies = [ - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, -] -sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/27/1d/297ff2c7ea50a768a2247621d6451abb2a07c0e9be7ca6d36ebe371658e5/pandas_stubs-3.0.0.260204.tar.gz", hash = "sha256:bf9294b76352effcffa9cb85edf0bed1339a7ec0c30b8e1ac3d66b4228f1fbc3" } -wheels = [ - { url = "https://mirrors.cloud.tencent.com/pypi/packages/7c/2f/f91e4eee21585ff548e83358332d5632ee49f6b2dcd96cb5dca4e0468951/pandas_stubs-3.0.0.260204-py3-none-any.whl", hash = "sha256:5ab9e4d55a6e2752e9720828564af40d48c4f709e6a2c69b743014a6fcb6c241" }, -] - [[package]] name = "pefile" version = "2024.8.26" @@ -2728,11 +2288,11 @@ version = "6.20.0" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ { name = "altgraph" }, - { name = "macholib", marker = "sys_platform == 'darwin' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "macholib", marker = "sys_platform == 'darwin' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, { name = "packaging" }, - { name = "pefile", marker = "sys_platform == 'win32' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "pefile", marker = "sys_platform == 'win32' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, { name = "pyinstaller-hooks-contrib" }, - { name = "pywin32-ctypes", marker = "sys_platform == 'win32' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "pywin32-ctypes", marker = "sys_platform == 'win32' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, { name = "setuptools" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/46/60/d03d52e6690d4e9caf333dcd14550cde634ce6c118b3bc8fa3112c3186fd/pyinstaller-6.20.0.tar.gz", hash = "sha256:95c5c7e03d5d61e9dfb8ef259c699cf492bb1041beb6dbe83696608cec07347a" } @@ -2956,8 +2516,8 @@ version = "3.8.1" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ { name = "colorlog" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, { name = "omegaconf" }, { name = "opencv-python" }, { name = "pillow" }, @@ -2992,20 +2552,20 @@ name = "scikit-learn" version = "1.7.2" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "joblib", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "threadpoolctl", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "joblib", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "threadpoolctl", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda" } wheels = [ @@ -3046,62 +2606,62 @@ name = "scikit-learn" version = "1.8.0" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", @@ -3128,10 +2688,10 @@ resolution-markers = [ "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "joblib", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "scipy", version = "1.17.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "threadpoolctl", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "joblib", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "scipy", version = "1.17.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "threadpoolctl", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd" } wheels = [ @@ -3178,17 +2738,17 @@ name = "scipy" version = "1.15.3" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf" } wheels = [ @@ -3244,62 +2804,62 @@ name = "scipy" version = "1.17.1" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", @@ -3326,7 +2886,7 @@ resolution-markers = [ "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0" } wheels = [ @@ -3406,8 +2966,8 @@ name = "shapely" version = "2.1.2" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/4d/bc/0989043118a27cccb4e906a46b7565ce36ca7b57f5a18b78f4f1b0f72d9d/shapely-2.1.2.tar.gz", hash = "sha256:2ed4ecb28320a433db18a5bf029986aa8afcfd740745e78847e330d5d94922a9" } wheels = [ @@ -3523,20 +3083,24 @@ version = "2.11.0" source = { registry = "https://download.pytorch.org/whl/cpu" } resolution-markers = [ "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin'", "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin'", "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin'", "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin'", "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin'", ] dependencies = [ - { name = "filelock", marker = "platform_machine != 's390x' and sys_platform == 'darwin'" }, - { name = "fsspec", marker = "platform_machine != 's390x' and sys_platform == 'darwin'" }, - { name = "jinja2", marker = "platform_machine != 's390x' and sys_platform == 'darwin'" }, - { name = "networkx", version = "3.4.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version >= '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "networkx", version = "3.6.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version < '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (python_full_version < '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "setuptools", marker = "platform_machine != 's390x' and sys_platform == 'darwin'" }, - { name = "sympy", marker = "platform_machine != 's390x' and sys_platform == 'darwin'" }, - { name = "typing-extensions", marker = "platform_machine != 's390x' and sys_platform == 'darwin'" }, + { name = "filelock", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (platform_machine != 's390x' and sys_platform == 'darwin')" }, + { name = "fsspec", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (platform_machine != 's390x' and sys_platform == 'darwin')" }, + { name = "jinja2", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (platform_machine != 's390x' and sys_platform == 'darwin')" }, + { name = "networkx", version = "3.4.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "networkx", version = "3.6.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "setuptools", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (platform_machine != 's390x' and sys_platform == 'darwin')" }, + { name = "sympy", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (platform_machine != 's390x' and sys_platform == 'darwin')" }, + { name = "typing-extensions", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (platform_machine != 's390x' and sys_platform == 'darwin')" }, ] wheels = [ { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91209c7d8a2460b76e8ff5b28b7623da4ab1d27474b79e1de83e954871985afe", upload-time = "2026-03-23T15:16:50Z" }, @@ -3559,7 +3123,6 @@ resolution-markers = [ "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin'", "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32'", "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32'", "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32'", @@ -3578,22 +3141,19 @@ resolution-markers = [ "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin'", "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin'", "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin'", "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin'", ] dependencies = [ - { name = "filelock", marker = "platform_machine == 's390x' or sys_platform != 'darwin'" }, - { name = "fsspec", marker = "platform_machine == 's390x' or sys_platform != 'darwin'" }, - { name = "jinja2", marker = "platform_machine == 's390x' or sys_platform != 'darwin'" }, - { name = "networkx", version = "3.4.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "networkx", version = "3.6.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "setuptools", marker = "platform_machine == 's390x' or sys_platform != 'darwin'" }, - { name = "sympy", marker = "platform_machine == 's390x' or sys_platform != 'darwin'" }, - { name = "typing-extensions", marker = "platform_machine == 's390x' or sys_platform != 'darwin'" }, + { name = "filelock", marker = "(python_full_version < '3.11' and platform_machine == 's390x') or sys_platform != 'darwin'" }, + { name = "fsspec", marker = "(python_full_version < '3.11' and platform_machine == 's390x') or sys_platform != 'darwin'" }, + { name = "jinja2", marker = "(python_full_version < '3.11' and platform_machine == 's390x') or sys_platform != 'darwin'" }, + { name = "networkx", version = "3.4.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "networkx", version = "3.6.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "setuptools", marker = "(python_full_version < '3.11' and platform_machine == 's390x') or sys_platform != 'darwin'" }, + { name = "sympy", marker = "(python_full_version < '3.11' and platform_machine == 's390x') or sys_platform != 'darwin'" }, + { name = "typing-extensions", marker = "(python_full_version < '3.11' and platform_machine == 's390x') or sys_platform != 'darwin'" }, ] wheels = [ { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.11.0%2Bcpu-cp310-cp310-linux_s390x.whl", hash = "sha256:3d8a7789e61dbf11f8922672c43354614b9b0debd40899c0a94f1ad9e0bd6bd9", upload-time = "2026-04-27T21:55:13Z" }, @@ -3659,13 +3219,13 @@ resolution-markers = [ "python_full_version < '3.11' and platform_machine == 's390x'", ] dependencies = [ - { name = "cuda-bindings", version = "12.9.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "sys_platform == 'linux'" }, - { name = "cuda-toolkit", version = "12.8.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, extra = ["cublas", "cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "(sys_platform == 'linux' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "cuda-bindings", marker = "sys_platform == 'linux'" }, + { name = "cuda-toolkit", extra = ["cublas", "cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "(sys_platform == 'linux' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, { name = "filelock" }, { name = "fsspec" }, { name = "jinja2" }, - { name = "networkx", version = "3.4.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "networkx", version = "3.6.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "networkx", version = "3.4.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "networkx", version = "3.6.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, { name = "nvidia-cudnn-cu12", marker = "sys_platform == 'linux'" }, { name = "nvidia-cusparselt-cu12", marker = "sys_platform == 'linux'" }, { name = "nvidia-nccl-cu12", marker = "sys_platform == 'linux'" }, @@ -3699,95 +3259,26 @@ wheels = [ { url = "https://download-r2.pytorch.org/whl/cu128/torch-2.11.0%2Bcu128-cp314-cp314t-win_amd64.whl", hash = "sha256:d86c125d720c2c368c53bd1a4ef062916d91fa965c10448c74c78b5d039faf2d", upload-time = "2026-04-27T18:01:14Z" }, ] -[[package]] -name = "torch" -version = "2.11.0+cu130" -source = { registry = "https://download.pytorch.org/whl/cu130" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version < '3.11' and platform_machine != 's390x'", - "python_full_version < '3.11' and platform_machine == 's390x'", -] -dependencies = [ - { name = "cuda-bindings", version = "13.2.0", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "sys_platform == 'linux'" }, - { name = "cuda-toolkit", version = "13.0.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, extra = ["cublas", "cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "(sys_platform == 'linux' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "filelock" }, - { name = "fsspec" }, - { name = "jinja2" }, - { name = "networkx", version = "3.4.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "networkx", version = "3.6.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "nvidia-cudnn-cu13", marker = "sys_platform == 'linux'" }, - { name = "nvidia-cusparselt-cu13", marker = "sys_platform == 'linux'" }, - { name = "nvidia-nccl-cu13", marker = "sys_platform == 'linux'" }, - { name = "nvidia-nvshmem-cu13", marker = "sys_platform == 'linux'" }, - { name = "setuptools" }, - { name = "sympy" }, - { name = "triton", marker = "sys_platform == 'linux'" }, - { name = "typing-extensions" }, -] -wheels = [ - { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:4af01fad0822353e766770ff2c7d6bdc2cbcc2ac7fcd6da93a9e3c6f3f932b21", upload-time = "2026-04-27T19:56:16Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:4c5be01584b7fee22d3c0d04062fd28026044acd07ffd0ee64cbd54b60e62d39", upload-time = "2026-04-27T19:56:40Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp310-cp310-win_amd64.whl", hash = "sha256:5b603a44f34816e18df254443a1fbfb4eef7d57128e5c7f6655f7fab45071f6e", upload-time = "2026-04-27T19:57:45Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:6304535e9e4cd1beeab449e407712602aa473a97e7b310dc5650ef50940bd94f", upload-time = "2026-04-27T19:58:56Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:225b22e0a4e36ea573d3a68796e6816a160616f67e8b8c55683a88bf7777f4cd", upload-time = "2026-04-27T19:59:21Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp311-cp311-win_amd64.whl", hash = "sha256:a1ff66c0ad21bf48c3187e84a08a6895d48e9bae435e27811b0b65f36bef4555", upload-time = "2026-04-27T20:00:28Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:252f237d417fac3ba59b1635815c1f035a8241f2af038f2c076ed430932d89f1", upload-time = "2026-04-27T20:01:46Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:96911323dcfcd42028c7e8edde7bdf25bb187753234e8775f0f3f112e86a22db", upload-time = "2026-04-27T20:02:14Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp312-cp312-win_amd64.whl", hash = "sha256:ef8beae16d781c3244ef28dc7bee6d8871c26bbde65d5bf66e902cb61972c4ab", upload-time = "2026-04-27T20:03:28Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:c3d60f79666b9101e3914a2e5dec2e81eac834e13cae0bcf59e94dc1a465f756", upload-time = "2026-04-27T20:04:49Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:554461b76f21211927c776056bcb0b00fb42972364794b686d768ebb0b586366", upload-time = "2026-04-27T20:05:21Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp313-cp313-win_amd64.whl", hash = "sha256:339801f2163698a53c7fb3c91883e7f44331d22c34d45acfbce4eff71f2332fa", upload-time = "2026-04-27T20:06:44Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:a33905bc3e093b25d2b019181cf834f7f7d4c562739e13dd36a798ecb2e411b0", upload-time = "2026-04-27T20:08:23Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:6fd10ed484eb695312ae829719888bb9f6c7f5e8503528e3e8ad1b98a45296c2", upload-time = "2026-04-27T20:08:56Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp313-cp313t-win_amd64.whl", hash = "sha256:21d2734fd02af45d19bb88c0ff2e86b238ce73f7bde6003ade7f1454ae299198", upload-time = "2026-04-27T20:10:20Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:efcdfe08ec2c9db28b50cc7329fed0c90bb74fa6fbce0f7eb12e20db2279a40f", upload-time = "2026-04-27T20:11:48Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:6ccc36928fd17c86011b46fb81bd2c85475f1fbf967dde758672d6a8d83a212a", upload-time = "2026-04-27T20:12:18Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp314-cp314-win_amd64.whl", hash = "sha256:d886f1c2f4406d7ad0c59f254ceb0a9c47a03e97a7c704b778a2066d752dde29", upload-time = "2026-04-27T20:13:41Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:bdb20f8b04e9fcaba2f354c3026667bebb74de8a92526b706aa735e2df334c24", upload-time = "2026-04-27T20:15:02Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:28f952cd4a927616ad9d77644a93237d1ca50bf30d0cf26962b9162d8a00ffa0", upload-time = "2026-04-27T20:15:30Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp314-cp314t-win_amd64.whl", hash = "sha256:d0a857adc487f275bfc9e7cdc51d12940613ba18b6362da214e20e9e3871f817", upload-time = "2026-04-27T20:16:48Z" }, -] - [[package]] name = "torchvision" version = "0.26.0" source = { registry = "https://download.pytorch.org/whl/cpu" } resolution-markers = [ "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin'", "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin'", "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin'", "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin'", "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version >= '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version < '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (python_full_version < '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "pillow", marker = "platform_machine != 's390x' and sys_platform == 'darwin'" }, - { name = "torch", version = "2.11.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "platform_machine != 's390x' and sys_platform == 'darwin'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "pillow", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (platform_machine != 's390x' and sys_platform == 'darwin')" }, + { name = "torch", version = "2.11.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (platform_machine != 's390x' and sys_platform == 'darwin')" }, ] wheels = [ { url = "https://download-r2.pytorch.org/whl/cpu/torchvision-0.26.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a06d4772a8e13e772906ed736cc53ec6639e5e60554f8e5fa6ca165aabebc464", upload-time = "2026-03-23T15:36:09Z" }, @@ -3810,7 +3301,6 @@ resolution-markers = [ "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin'", "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32'", "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32'", "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32'", @@ -3829,18 +3319,15 @@ resolution-markers = [ "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin'", "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin'", "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin'", "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "pillow", marker = "platform_machine == 's390x' or sys_platform != 'darwin'" }, - { name = "torch", version = "2.11.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "platform_machine == 's390x' or sys_platform != 'darwin'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "pillow", marker = "(python_full_version < '3.11' and platform_machine == 's390x') or sys_platform != 'darwin'" }, + { name = "torch", version = "2.11.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(python_full_version < '3.11' and platform_machine == 's390x') or sys_platform != 'darwin'" }, ] wheels = [ { url = "https://download-r2.pytorch.org/whl/cpu/torchvision-0.26.0%2Bcpu-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:97df9a8595dce256d2e6dd16bbcd1c68dd00eec712e37d4b6ec7985453ddc2aa", upload-time = "2026-03-23T15:36:09Z" }, @@ -3899,8 +3386,8 @@ resolution-markers = [ "python_full_version < '3.11' and platform_machine == 's390x'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, { name = "pillow" }, { name = "torch", version = "2.11.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" } }, ] @@ -3928,74 +3415,12 @@ wheels = [ { url = "https://download-r2.pytorch.org/whl/cu128/torchvision-0.26.0%2Bcu128-cp314-cp314t-win_amd64.whl", hash = "sha256:f160dc552a086244f7102c898f7be8ef46a41b36bce5ea80a4f2493cb30ca1fc", upload-time = "2026-04-09T23:21:41Z" }, ] -[[package]] -name = "torchvision" -version = "0.26.0+cu130" -source = { registry = "https://download.pytorch.org/whl/cu130" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version < '3.11' and platform_machine != 's390x'", - "python_full_version < '3.11' and platform_machine == 's390x'", -] -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "pillow" }, - { name = "torch", version = "2.11.0+cu130", source = { registry = "https://download.pytorch.org/whl/cu130" } }, -] -wheels = [ - { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:3094bed175eee817f9fc61d1c8bdecb1d807c25f49517fbfe60f15d24135fcde", upload-time = "2026-03-23T15:36:25Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:7d820708732d2467caf2ef16c3ad60c8ff402bd05bd246b108ee080f3fbfdc6e", upload-time = "2026-03-23T15:36:25Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp310-cp310-win_amd64.whl", hash = "sha256:a410cf52350ff4a5e0a71cedaf03bdec973c7a22421f36163d887a251acf51d7", upload-time = "2026-03-23T15:36:25Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:31f87cd00c09e071980d6a4ce218289a73302ad6a7ce0b3b62a74a4081fc339d", upload-time = "2026-03-23T15:36:25Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:3b53e3b611561e03ac261d06cb3f38782120ad9e0b4cd9f01549799097c713a6", upload-time = "2026-03-23T15:36:25Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp311-cp311-win_amd64.whl", hash = "sha256:d019b9d02433515d59ad403d8a6f521da7844c030d6c8003eeec39e15e59f9fa", upload-time = "2026-04-09T23:21:52Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:e2b39db78be674ee4ce7e921f54b70e5c281594c9267d981c061684ed38df936", upload-time = "2026-03-23T15:36:26Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:0f030a9bd8ada1a31b7111ea1589c1ecb5fa0884fee700a203e731b4cf378a98", upload-time = "2026-03-23T15:36:26Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp312-cp312-win_amd64.whl", hash = "sha256:a3578f7c8e8a2724306c68c56873a1675fa7ce45471e18235c720a2ed242fe44", upload-time = "2026-04-09T23:21:53Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:3af2c699719cc0e2518bf317664200e5a987fb75a25b9b3bf3817a4796ddd64f", upload-time = "2026-03-23T15:36:26Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:441a98bed4fff1d54b8450499e377e1a605bec31f2ecb1a38a340f95dcc83897", upload-time = "2026-03-23T15:36:26Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp313-cp313-win_amd64.whl", hash = "sha256:64de855465d6de60583e776889fad9412480f9f9e04fdd8d17ae96fa93864e9a", upload-time = "2026-04-09T23:21:54Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:c3ac485da79552b4f579c525c826f7a63288b0d1cafc1201b16e1148bfdea69a", upload-time = "2026-03-23T15:36:26Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:110659ff38cd1d2ca0ac6e6a0f2c842fcb5fe739dfe65ff7456a12b2c4dce775", upload-time = "2026-03-23T15:36:26Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp313-cp313t-win_amd64.whl", hash = "sha256:a7e19c3ab5c6d8e3c9f8c6d427f6b8862dfb8227ea4a758ea7a709951daf2f0d", upload-time = "2026-04-09T23:21:55Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:13fe3dee74a9ee31b551b10a8b4113d9bc5212bb0572a07af88b34a5d25d9701", upload-time = "2026-03-23T15:36:26Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:8d6a83a639d14f7e84f6b838a17e26f9ce41cdbe3dfe0c29ef74b32eb398ba28", upload-time = "2026-03-23T15:36:26Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp314-cp314-win_amd64.whl", hash = "sha256:ab671ffe837aff470baad6af97133ea5a49f8ea2383832550e510a63caf711e4", upload-time = "2026-04-09T23:21:56Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:cd89effa98de436ec22ccbbd278cdadc0fdec8eb81a396150f50b321c2230866", upload-time = "2026-03-23T15:36:26Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:aacac4d990ac794f3abeca66cc26affb42fbeba9789e5c351183665bab4902d2", upload-time = "2026-03-23T15:36:26Z" }, - { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp314-cp314t-win_amd64.whl", hash = "sha256:23b9666084c72d07fc715001880b648e6a410796d1925c10f054f1ee034f5cc7", upload-time = "2026-04-09T23:21:57Z" }, -] - [[package]] name = "tqdm" version = "4.67.3" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "colorama", marker = "sys_platform == 'win32' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb" } wheels = [ @@ -4023,24 +3448,6 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/f6/56/6113c23ff46c00aae423333eb58b3e60bdfe9179d542781955a5e1514cb3/triton-3.6.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46bd1c1af4b6704e554cad2eeb3b0a6513a980d470ccfa63189737340c7746a7" }, ] -[[package]] -name = "types-pytz" -version = "2026.1.1.20260408" -source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } -sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/f7/b7/33f5a4f29b1f285b99ff79a607751a7996194cbb98705e331dab7a2daa28/types_pytz-2026.1.1.20260408.tar.gz", hash = "sha256:89b6a34b9198ea2a4b98a9d15cbca987053f52a105fd44f7ce3789cae4349408" } -wheels = [ - { url = "https://mirrors.cloud.tencent.com/pypi/packages/ae/90/12c059e6bb330a22d9cc97daf027ac7fb7f50fbf518e4d88185b4d39120e/types_pytz-2026.1.1.20260408-py3-none-any.whl", hash = "sha256:c7e4dec76221fb7d0c97b91ad8561d689bebe39b6bcb7b728387e7ffd8cde788" }, -] - -[[package]] -name = "types-toml" -version = "0.10.8.20260408" -source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } -sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/71/9b/887564a51a84c96ba08b715570e546f0ea793df6372b736bfbc596ca5536/types_toml-0.10.8.20260408.tar.gz", hash = "sha256:6b30b031235565a12febb1388900b129f1adeabfcfa594da46d0372b2ac107ad" } -wheels = [ - { url = "https://mirrors.cloud.tencent.com/pypi/packages/56/f1/942d95ba026779bc6e3064f8b094216588dc3276cc328cf8e03a0541918d/types_toml-0.10.8.20260408-py3-none-any.whl", hash = "sha256:e958d4c660385e548705a298f17dc162baf44c8b6d6aff79aeefe75f4f77ac87" }, -] - [[package]] name = "typing-extensions" version = "4.15.0" @@ -4073,8 +3480,8 @@ name = "windows-capture" version = "2.0.0" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, { name = "opencv-python" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/7e/60/1acdf4a4bb625bf04e94afde8bdb60a06997e644c6be7befe413dc5215ed/windows_capture-2.0.0.tar.gz", hash = "sha256:f1d587c182afa06c060e36ba2bd4f216a8ed03f320f51d7b6d5c8a2759469956" } diff --git a/src/models/val.py b/val.py similarity index 71% rename from src/models/val.py rename to val.py index f91a52f..2d83354 100644 --- a/src/models/val.py +++ b/val.py @@ -1,8 +1,7 @@ import torch import torch.nn as nn from torch.utils.data import DataLoader -from src.models.train import UnitAwareTransformer, ArknightsDataset - +from train import UnitAwareTransformer,ArknightsDataset def evaluate(model, data_loader, criterion, device): model.eval() @@ -17,16 +16,8 @@ def evaluate(model, data_loader, criterion, device): # 其余代码保持不变... # 检查输入值范围 - if ( - torch.isnan(ls).any() - or torch.isnan(lc).any() - or torch.isnan(rs).any() - or torch.isnan(rc).any() - or torch.isinf(ls).any() - or torch.isinf(lc).any() - or torch.isinf(rs).any() - or torch.isinf(rc).any() - ): + if torch.isnan(ls).any() or torch.isnan(lc).any() or torch.isnan(rs).any() or torch.isnan(rc).any() or \ + torch.isinf(ls).any() or torch.isinf(lc).any() or torch.isinf(rs).any() or torch.isinf(rc).any(): print("警告: 评估时输入数据包含NaN或Inf,跳过该批次") continue @@ -63,15 +54,14 @@ def evaluate(model, data_loader, criterion, device): return total_loss / max(1, len(data_loader)), 100 * correct / max(1, total) - def main(): config = { - "data_file": "arknights_clean.csv", - "batch_size": 256, - "max_feature_value": 200, # 限制特征最大值,防止极端值造成不稳定 + 'data_file': 'arknights_clean.csv', + 'batch_size': 256, + 'max_feature_value': 200 # 限制特征最大值,防止极端值造成不稳定 } # 设置设备 - device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') print(f"使用设备: {device}") # 检查CUDA可用性 @@ -85,41 +75,36 @@ def main(): torch.backends.cudnn.benchmark = False else: print("警告: 未检测到GPU,将在CPU上运行训练,这可能会很慢!") - + dataset = ArknightsDataset( - config["data_file"], - max_value=config["max_feature_value"], # 使用最大值限制 + config['data_file'], + max_value=config['max_feature_value'] # 使用最大值限制 ) - + # 修改这里 - 只有当数据在CPU上时才使用pin_memory val_loader = DataLoader( - dataset, - batch_size=config["batch_size"], + dataset, + batch_size=config['batch_size'], num_workers=0, - pin_memory=False, # 禁用pin_memory,或者根据你的数据集情况调整 + pin_memory=False # 禁用pin_memory,或者根据你的数据集情况调整 ) - + model = UnitAwareTransformer( num_units=34, # 更新为34个怪物 embed_dim=128, num_heads=8, - num_layers=4, # 注意:train.py中config['n_layers']=4 + num_layers=4 # 注意:train.py中config['n_layers']=4 ).to(device) # 加载模型权重 try: - model = torch.load( - "models/best_model_full.pth", - map_location=device, - weights_only=False, - ) + model = torch.load('models/best_model_full.pth', map_location=device, weights_only=False) except TypeError: # 如果旧版本 PyTorch 不认识 weights_only - model = torch.load("models/best_model_full.pth", map_location=device) + model = torch.load('models/best_model_full.pth', map_location=device) model.eval() criterion = nn.BCELoss() val_loss, val_acc = evaluate(model, val_loader, criterion, device) print(f"Val Loss: {val_loss:.4f} | Acc: {val_acc:.2f}%") - - + if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/src/game/winrt_capture.py b/winrt_capture.py similarity index 90% rename from src/game/winrt_capture.py rename to winrt_capture.py index 38ef03e..f69847d 100644 --- a/src/game/winrt_capture.py +++ b/winrt_capture.py @@ -11,7 +11,6 @@ - 运行时可调用 `recreate()` **切换捕获目标**(重新创建底层会话); - 通过锁保护 start/stop/recreate 的并发安全。 """ - from __future__ import annotations import threading import time @@ -23,11 +22,10 @@ import numpy as np from windows_capture import WindowsCapture, Frame, InternalCaptureControl -from PyQt6.QtWidgets import QVBoxLayout, QListWidget, QLineEdit, QDialog +from PyQt6.QtWidgets import (QVBoxLayout,QListWidget,QLineEdit,QDialog) logger = logging.getLogger(__name__) - class WinRTScreenCapture: """Windows.Graphics.Capture 适配器。 @@ -63,9 +61,7 @@ def __init__( # 控制状态 self._started = False - self._control: Optional[InternalCaptureControl] = ( - None # 保存 CaptureControl,用于停止 - ) + self._control: Optional[InternalCaptureControl] = None # 保存 CaptureControl,用于停止 # 保护 start/stop/recreate 的可重入锁 self._ctl_lock = threading.RLock() @@ -122,9 +118,7 @@ def start(self) -> None: cursor_capture=self._init_kwargs["cursor_capture"], draw_border=self._init_kwargs["draw_border"], secondary_window=None, - minimum_update_interval=self._init_kwargs[ - "minimum_update_interval" - ], + minimum_update_interval=self._init_kwargs["minimum_update_interval"], dirty_region=None, monitor_index=1, window_name=None, @@ -151,11 +145,7 @@ def stop(self) -> None: with self._lock: self._latest = None - def recreate( - self, - window_name: Optional[str] = None, - monitor_index: Optional[int] = None, - ) -> None: + def recreate(self, window_name: Optional[str] = None, monitor_index: Optional[int] = None) -> None: """运行时切换捕获目标并立即启动。 同步步骤:stop → 以新参数创建 → 重新绑定事件 → 启动。 @@ -166,9 +156,7 @@ def recreate( cursor_capture=self._init_kwargs["cursor_capture"], draw_border=self._init_kwargs["draw_border"], secondary_window=None, - minimum_update_interval=self._init_kwargs[ - "minimum_update_interval" - ], + minimum_update_interval=self._init_kwargs["minimum_update_interval"], dirty_region=None, monitor_index=monitor_index if (window_name is None) else None, window_name=window_name, @@ -206,9 +194,7 @@ def wait_first_frame(self, timeout_sec: float = 3.0) -> bool: time.sleep(0.01) return False - def snapshot( - self, bbox: Optional[tuple[int, int, int, int]] = None - ) -> np.ndarray: + def snapshot(self, bbox: Optional[tuple[int, int, int, int]] = None) -> np.ndarray: """返回最近一帧的 **BGR** 拷贝图像,可选裁剪到指定 bbox。 若尚未有首帧,将抛出 `RuntimeError`。 @@ -216,7 +202,7 @@ def snapshot( with self._lock: if self._latest is None: raise RuntimeError("WinRT capture 尚未产生首帧") - + frame = self._latest.copy() if bbox: x1, y1, x2, y2 = bbox @@ -228,11 +214,7 @@ def snapshot( return frame[y1:y2, x1:x2] return frame - def snapshot_once( - self, - bbox: Optional[tuple[int, int, int, int]] = None, - timeout_sec: float = 2.0, - ) -> np.ndarray: + def snapshot_once(self, bbox: Optional[tuple[int, int, int, int]] = None, timeout_sec: float = 2.0) -> np.ndarray: """返回最近一帧的 **BGR** 拷贝图像,可选裁剪到指定 bbox。 若当前未启动,则会临时启动捕获,获取一帧后立即停止。 @@ -241,7 +223,7 @@ def snapshot_once( was_started = self._started if not was_started: self.start() - + frame = None t0 = time.time() while time.time() - t0 < timeout_sec: @@ -250,8 +232,8 @@ def snapshot_once( frame = self._latest.copy() break time.sleep(0.01) - - if frame is None: # 超时 + + if frame is None: # 超时 if not was_started: self.stop() raise RuntimeError("WinRT capture 尚未产生首帧") @@ -269,11 +251,7 @@ def snapshot_once( self.stop() return frame - def set_capture_target( - self, - window_name: Optional[str] = None, - monitor_index: Optional[int] = None, - ) -> None: + def set_capture_target(self, window_name: Optional[str] = None, monitor_index: Optional[int] = None) -> None: """ 设置 WinRT 截屏目标(窗口标题或整屏),并启动捕获,等待首帧。 若初始化失败,将抛出异常。 @@ -281,15 +259,11 @@ def set_capture_target( with self._ctl_lock: if self._started: # 如果已经启动,则重建底层会话 - self.recreate( - window_name=window_name, monitor_index=monitor_index - ) + self.recreate(window_name=window_name, monitor_index=monitor_index) else: # 否则,更新初始化参数并启动 self._init_kwargs.update( - monitor_index=( - monitor_index if (window_name is None) else None - ), + monitor_index=monitor_index if (window_name is None) else None, window_name=window_name, ) self._cap = WindowsCapture(**self._init_kwargs) @@ -302,13 +276,10 @@ def set_capture_target( self.stop() raise RuntimeError("WinRT capture 尚未产生首帧") - def list_visible_window_titles() -> list[str]: """列出所有**可见**窗口的标题(去重并按字典序排序)。""" EnumWindows = ctypes.windll.user32.EnumWindows - EnumWindowsProc = ctypes.WINFUNCTYPE( - wintypes.BOOL, wintypes.HWND, wintypes.LPARAM - ) + EnumWindowsProc = ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.HWND, wintypes.LPARAM) IsWindowVisible = ctypes.windll.user32.IsWindowVisible GetWindowTextW = ctypes.windll.user32.GetWindowTextW GetWindowTextLengthW = ctypes.windll.user32.GetWindowTextLengthW @@ -331,7 +302,6 @@ def foreach(hwnd, lParam): logger.info(f"窗口列表:{titles}") return titles - # ------------------------- 截屏源选择对话框 ------------------------- class WindowPickerDialog(QDialog): """列出可见窗口标题,并内置“整屏(1/2/3)”选项。双击确定。""" diff --git a/iron_shark_launcher.bat "b/\343\200\220\350\204\232\346\234\254\343\200\221\351\223\201\351\262\250\351\261\274\345\220\257\345\212\250\357\274\201(\345\270\246PyTorch\347\211\210\346\234\254\351\200\211\346\213\251\347\211\210).bat" similarity index 62% rename from iron_shark_launcher.bat rename to "\343\200\220\350\204\232\346\234\254\343\200\221\351\223\201\351\262\250\351\261\274\345\220\257\345\212\250\357\274\201(\345\270\246PyTorch\347\211\210\346\234\254\351\200\211\346\213\251\347\211\210).bat" index b58d5b2..542044f 100644 --- a/iron_shark_launcher.bat +++ "b/\343\200\220\350\204\232\346\234\254\343\200\221\351\223\201\351\262\250\351\261\274\345\220\257\345\212\250\357\274\201(\345\270\246PyTorch\347\211\210\346\234\254\351\200\211\346\213\251\347\211\210).bat" @@ -1,81 +1,73 @@ -@echo off -chcp 65001 >nul -title CannotMax -echo 请按任意键启动CannotMax... -pause >nul - -set "current_dir=%cd%" - -:: 检查 uv 是否存在 -where uv >nul 2>nul -if %errorlevel% equ 0 goto run_main - -:: 安装 uv -echo 未检测到 uv,正在安装... -powershell -ExecutionPolicy Bypass -Command "irm https://gitee.com/wangnov/uv-custom/releases/download/latest/uv-installer-custom.ps1 | iex" - -call :refresh_path -:: 验证 uv 是否可用 -where uv >nul 2>nul -if %errorlevel% neq 0 ( - echo 安装 uv 后仍未找到,请检查安装路径 - pause - exit /b 1 -) -:: =================================== - -:run_main -cd /d "%current_dir%" - -:: ===== 选择推理环境选项询问 ===== -set "torch_choice=none" -echo. -echo 选择推理环境? (5秒后自动跳过) -echo C/c - Pytorch CPU版本 -echo D/d - Pytorch CUDA 12.8版本 -echo E/e - Pytorch CUDA 13.0版本 -echo N/n - 使用onnxruntime(默认) -echo ------------------------------------ - -:: 使用choice命令实现带超时的输入 -choice /c CDEN /t 5 /d N /n >nul -:: choice 的 errorlevel 对应顺序为: C=1, D=2, E=3, N=4 -if errorlevel 4 ( - set "torch_choice=none" -) else if errorlevel 3 ( - set "torch_choice=cu130" -) else if errorlevel 2 ( - set "torch_choice=cu128" -) else ( - set "torch_choice=cpu" -) - -:: 根据选择使用对应环境运行主程序 -if "%torch_choice%"=="cpu" ( - echo 使用Pytorch CPU版本... - uv sync --extra cpu -) else if "%torch_choice%"=="cu128" ( - echo 使用Pytorch CUDA 12.8版本... - uv sync --extra cu128 -) else if "%torch_choice%"=="cu130" ( - echo 使用Pytorch CUDA 13.0版本... - uv sync --extra cu130 -) else ( - echo 使用onnxruntime... - uv sync -) -echo. - -:: =================================== -uv run main.py - -echo 主程序已退出,感谢您的使用! -pause >nul -exit /b - -:: 刷新 PATH 的函数 -:refresh_path -for /f "skip=2 tokens=3*" %%a in ('reg query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v Path 2^>nul') do set "SYSTEM_PATH=%%a %%b" -for /f "skip=2 tokens=3*" %%a in ('reg query "HKCU\Environment" /v Path 2^>nul') do set "USER_PATH=%%a %%b" -set "PATH=%USER_PATH%;%SYSTEM_PATH%" +@echo off +chcp 65001 >nul +title CannotMax +echo 请按任意键启动CannotMax... +pause >nul + +set "current_dir=%cd%" + +:: 检查 uv 是否存在 +where uv >nul 2>nul +if %errorlevel% equ 0 goto run_main + +:: 安装 uv +echo 未检测到 uv,正在安装... +powershell -ExecutionPolicy Bypass -Command "irm https://gitee.com/wangnov/uv-custom/releases/download/latest/uv-installer-custom.ps1 | iex" + +call :refresh_path +:: 验证 uv 是否可用 +where uv >nul 2>nul +if %errorlevel% neq 0 ( + echo 安装 uv 后仍未找到,请检查安装路径 + pause + exit /b 1 +) +:: =================================== + +:run_main +cd /d "%current_dir%" + +:: ===== PyTorch安装选项询问 ===== +set "torch_choice=none" +echo. +echo 是否需要安装PyTorch? (5秒后自动跳过) +echo C/c - CPU版本 +echo G/g - CUDA版本 +echo N/n - 跳过安装 +echo ------------------------------------ + +:: 使用choice命令实现带超时的输入 +choice /c CGN /t 5 /d N /n >nul +if errorlevel 3 ( + set "torch_choice=none" +) else if errorlevel 2 ( + set "torch_choice=cuda" +) else if errorlevel 1 ( + set "torch_choice=cpu" +) + +:: 根据选择安装PyTorch +if "%torch_choice%"=="cpu" ( + echo 安装CPU版本的PyTorch... + uv add torch torchvision --extra cpu +) else if "%torch_choice%"=="cuda" ( + echo 安装CUDA版本的PyTorch... + uv add torch torchvision --extra cu128 +) else ( + echo 跳过PyTorch安装 +) +echo. + +:: =================================== +uv run main.py + +echo 主程序已退出,感谢您的使用! +pause >nul +exit /b + +:: 刷新 PATH 的函数 +:refresh_path +for /f "skip=2 tokens=3*" %%a in ('reg query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v Path 2^>nul') do set "SYSTEM_PATH=%%a %%b" +for /f "skip=2 tokens=3*" %%a in ('reg query "HKCU\Environment" /v Path 2^>nul') do set "USER_PATH=%%a %%b" +set "PATH=%USER_PATH%;%SYSTEM_PATH%" exit /b \ No newline at end of file From bb23ed1e242ed0951850f85d5f48eda08d762ff4 Mon Sep 17 00:00:00 2001 From: 1ZUMIKun <68433151+1ZUMIKun@users.noreply.github.com> Date: Fri, 1 May 2026 12:36:30 +0800 Subject: [PATCH 6/9] Update monster data Co-authored-by: Copilot --- auto_fetch.py | 33 +- monster_greenvine.csv | 18 + uv.lock | 1647 ++++++++++++++++++++++++++--------------- 3 files changed, 1084 insertions(+), 614 deletions(-) diff --git a/auto_fetch.py b/auto_fetch.py index a17cf02..9c758ac 100644 --- a/auto_fetch.py +++ b/auto_fetch.py @@ -726,28 +726,27 @@ def start_auto_fetch(self): self._log(logging.INFO, f"创建文件夹: {self.data_folder}") self.data_folder.mkdir(parents=True, exist_ok=True) # 创建文件夹 (self.data_folder / "images").mkdir(parents=True, exist_ok=True) - with open(self.data_folder / "arknights.csv", "w", newline="") as file: - # 创建CSV表头 - if self.field_recognizer is not None: - # 获取场地特征列数 - num_field_features = len(self.field_recognizer.get_feature_columns()) - - # 按照data_cleaning_with_field_recognize_gpu.py的格式创建表头 - header = [f"{i+1}L" for i in range(MONSTER_COUNT)] # 1L-77L - header += [f"{i+1}LF" for i in range(MONSTER_COUNT, MONSTER_COUNT + num_field_features)] # 78LF-83LF (场地特征) - header += [f"{i+1}R" for i in range(MONSTER_COUNT)] # 1R-77R - header += [f"{i+1}RF" for i in range(MONSTER_COUNT, MONSTER_COUNT + num_field_features)] # 78RF-83RF (场地特征) + + def get_expected_header(): + """根据配置生成预期表头""" + if FIELD_FEATURE_COUNT > 0: + header = [f"{i + 1}L" for i in range(MONSTER_COUNT)] + header += [f"{i + 1}LF" for i in range(MONSTER_COUNT, MONSTER_COUNT + FIELD_FEATURE_COUNT)] + header += [f"{i + 1}R" for i in range(MONSTER_COUNT)] + header += [f"{i + 1}RF" for i in range(MONSTER_COUNT, MONSTER_COUNT + FIELD_FEATURE_COUNT)] header += ["Result", "ImgPath"] - logger.info(f"创建包含场地特征的CSV表头,场地特征数: {num_field_features}") else: - # 仅怪物数据的格式 - header = [f"{i+1}L" for i in range(MONSTER_COUNT)] # 左侧怪物数据 - header += [f"{i+1}R" for i in range(MONSTER_COUNT)] # 右侧怪物数据 + header = [f"{i + 1}L" for i in range(MONSTER_COUNT)] + header += [f"{i + 1}R" for i in range(MONSTER_COUNT)] header += ["Result", "ImgPath"] - logger.info("创建仅包含怪物数据的CSV表头") - + return header + + # 创建CSV表头 + with open(self.data_folder / "arknights.csv", "w", newline="") as file: + header = get_expected_header() writer = csv.writer(file) writer.writerow(header) + self.log_file_handler = logging.FileHandler( self.data_folder / f"AutoFetch_{start_time}.log", "a", "utf-8" ) diff --git a/monster_greenvine.csv b/monster_greenvine.csv index 478f509..50ab3e3 100644 --- a/monster_greenvine.csv +++ b/monster_greenvine.csv @@ -59,3 +59,21 @@ 58,侠客三人行,玉双剑,38000,700,,300,35,,,0.5,,,【协同】该敌人以组合形式出场\n比赛如同江湖行事,独来独往无法解决所有问题,侠客们对此心知肚明。 59,“萨米的意志”,“萨米的意志”,100000,800,,800,40,,,1,,,有它出场的比赛观众总能坚持观看到最后一局,因为离场通道被它堵住了。 60,岁相,岁相,130000,1200,,550,30,,,0.1,,,主办方在观众席前加装了许多层隔热玻璃,这才说服人们安心前来观赛。 +61,普通的萨卡兹,萨卡兹大剑手,3750,900,,230,50,0.8,2,0.425,7,0, +62,源石的腿脚,宿主流浪者,5500,975,,200,30,0.8,3,0.65,10,0,每秒恢复250生命值 +63,“火苗与软钢”,复仇者,9000,720,,230,50,,2.3,0.65,11,,半血后攻击2016 +64,锁链拳手,拳手囚犯,4000,450,,300,0,,1.2,0.8,,,禁锢状态下,攻击速度-50 进行第4次攻击前,切换至解放状态 解放状态下,攻击力+50%,获得60%防御无视 +65,衣架射手,11,,,,,,,,,,,无 +66,劈柴骑士,沸血骑士团精锐,6500,975,,800,0,,2.3,0.7,,,每当友方单位死亡时,攻击+10%,攻速+5,上限10层 +67,奔跑吧!瓢壳!,11,,,,,,,,,,,无 +68,硕鼷,11,,,,,,,,,,,无 +69,超级洗地机,11,,,,,,,,,,,无 +70,依然“狼之主”,11,,,,,,,,,,,无 +71,蟹蟹爷爷,“钳钳生风”,8500,1950,,2500,90,0.8,3,0.9,25,50,无 +72,标枪恐鱼,11,,,,,,,,,,,无 +73,食腐野兽,11,,,,,,,,,,,无 +74,调停的意志,11,,,,,,,,,,,无 +75,“食器时代”,11,,,,,,,,,,,无 +76,速胜卫士,弧光镜卫,,,,,,,,,,,无 +77,“交通亭”量产型,“交通亭”量产型,,,,,,,,,,,无 +78,零件卡门,11,,,,,,,,,,,无 \ No newline at end of file diff --git a/uv.lock b/uv.lock index 6c882ef..2b69784 100644 --- a/uv.lock +++ b/uv.lock @@ -2,68 +2,68 @@ version = 1 revision = 3 requires-python = ">=3.10" resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", @@ -94,6 +94,12 @@ resolution-markers = [ conflicts = [[ { package = "cannotmax-greenvine", extra = "cpu" }, { package = "cannotmax-greenvine", extra = "cu128" }, +], [ + { package = "cannotmax-greenvine", extra = "cpu" }, + { package = "cannotmax-greenvine", extra = "cu130" }, +], [ + { package = "cannotmax-greenvine", extra = "cu128" }, + { package = "cannotmax-greenvine", extra = "cu130" }, ]] [[package]] @@ -118,34 +124,38 @@ source = { virtual = "." } dependencies = [ { name = "maafw" }, { name = "matplotlib" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "onnxruntime", version = "1.24.3", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "onnxruntime", version = "1.25.0", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "onnxruntime", version = "1.24.3", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "onnxruntime", version = "1.25.0", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "opencv-python" }, - { name = "pandas", version = "2.3.3", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "pandas", version = "3.0.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "pandas", version = "2.3.3", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "pandas", version = "3.0.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "pillow" }, { name = "pyqt6" }, { name = "pywin32" }, { name = "rapidocr" }, - { name = "scikit-learn", version = "1.7.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "scikit-learn", version = "1.8.0", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "scikit-learn", version = "1.7.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "scikit-learn", version = "1.8.0", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "toml" }, { name = "windows-capture" }, ] [package.optional-dependencies] cpu = [ - { name = "torch", version = "2.11.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(python_full_version < '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version >= '3.11' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "torch", version = "2.11.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(python_full_version < '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu')" }, - { name = "torchvision", version = "0.26.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(python_full_version < '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version >= '3.11' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "torchvision", version = "0.26.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(python_full_version < '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu')" }, + { name = "torch", version = "2.11.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "torch", version = "2.11.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "torchvision", version = "0.26.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "torchvision", version = "0.26.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] cu128 = [ { name = "torch", version = "2.11.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" } }, { name = "torchvision", version = "0.26.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" } }, ] +cu130 = [ + { name = "torch", version = "2.11.0+cu130", source = { registry = "https://download.pytorch.org/whl/cu130" } }, + { name = "torchvision", version = "0.26.0+cu130", source = { registry = "https://download.pytorch.org/whl/cu130" } }, +] [package.dev-dependencies] dev = [ @@ -170,11 +180,13 @@ requires-dist = [ { name = "toml" }, { name = "torch", marker = "extra == 'cpu'", index = "https://download.pytorch.org/whl/cpu", conflict = { package = "cannotmax-greenvine", extra = "cpu" } }, { name = "torch", marker = "extra == 'cu128'", specifier = ">=2.7.0", index = "https://download.pytorch.org/whl/cu128", conflict = { package = "cannotmax-greenvine", extra = "cu128" } }, + { name = "torch", marker = "extra == 'cu130'", specifier = ">=2.7.0", index = "https://download.pytorch.org/whl/cu130", conflict = { package = "cannotmax-greenvine", extra = "cu130" } }, { name = "torchvision", marker = "extra == 'cpu'", index = "https://download.pytorch.org/whl/cpu", conflict = { package = "cannotmax-greenvine", extra = "cpu" } }, { name = "torchvision", marker = "extra == 'cu128'", specifier = ">=0.22.0", index = "https://download.pytorch.org/whl/cu128", conflict = { package = "cannotmax-greenvine", extra = "cu128" } }, + { name = "torchvision", marker = "extra == 'cu130'", specifier = ">=0.22.0", index = "https://download.pytorch.org/whl/cu130", conflict = { package = "cannotmax-greenvine", extra = "cu130" } }, { name = "windows-capture" }, ] -provides-extras = ["cpu", "cu128"] +provides-extras = ["cpu", "cu128", "cu130"] [package.metadata.requires-dev] dev = [ @@ -311,7 +323,7 @@ name = "colorlog" version = "6.10.1" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "colorama", marker = "sys_platform == 'win32' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/a2/61/f083b5ac52e505dfc1c624eafbf8c7589a0d7f32daa398d2e7590efa5fda/colorlog-6.10.1.tar.gz", hash = "sha256:eb4ae5cb65fe7fec7773c2306061a8e63e02efc2c72eba9d27b0fa23c94f1321" } wheels = [ @@ -323,17 +335,17 @@ name = "contourpy" version = "1.3.2" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54" } wheels = [ @@ -400,62 +412,62 @@ name = "contourpy" version = "1.3.3" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", @@ -482,7 +494,7 @@ resolution-markers = [ "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880" } wheels = [ @@ -563,6 +575,18 @@ wheels = [ name = "cuda-bindings" version = "12.9.6" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +resolution-markers = [ + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.11' and platform_machine != 's390x'", + "python_full_version < '3.11' and platform_machine == 's390x'", +] dependencies = [ { name = "cuda-pathfinder", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, ] @@ -590,6 +614,46 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/4b/98/8e5363d00c959d4172b1d619a4f03af454bf9952636224f0ac0f5c35c067/cuda_bindings-12.9.6-cp314-cp314t-win_amd64.whl", hash = "sha256:7f0a08eba6e807d041bf6f2ba66d84db1ddf54787399dfac716497ef40fb5fc3" }, ] +[[package]] +name = "cuda-bindings" +version = "13.2.0" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +resolution-markers = [ + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.11' and platform_machine != 's390x'", + "python_full_version < '3.11' and platform_machine == 's390x'", +] +dependencies = [ + { name = "cuda-pathfinder", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, +] +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/1a/fe/7351d7e586a8b4c9f89731bfe4cf0148223e8f9903ff09571f78b3fb0682/cuda_bindings-13.2.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08b395f79cb89ce0cd8effff07c4a1e20101b873c256a1aeb286e8fd7bd0f556" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/aa/ef/184aa775e970fc089942cd9ec6302e6e44679d4c14549c6a7ea45bf7f798/cuda_bindings-13.2.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6f3682ec3c4769326aafc67c2ba669d97d688d0b7e63e659d36d2f8b72f32d6" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/ec/ea/81999d01375645f34596c76eb046b4b36d58cc6fe2bddb2410f8a7b7a827/cuda_bindings-13.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:845025438a1b9e20718b9fb42add3e0eb72e85458bcab3eeb80bfd8f0a9dab33" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/e0/a9/3a8241c6e19483ac1f1dcf5c10238205dcb8a6e9d0d4d4709240dff28ff4/cuda_bindings-13.2.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:721104c603f059780d287969be3d194a18d0cc3b713ed9049065a1107706759d" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/e9/94/2748597f47bb1600cd466b20cab4159f1530a3a33fe7f70fee199b3abb9e/cuda_bindings-13.2.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1eba9504ac70667dd48313395fe05157518fd6371b532790e96fbb31bbb5a5e1" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/29/5a/0ce1731c48bcd9f40996a4ef1abbf634f1a7fe4a15c5050b1e75ce3a7acf/cuda_bindings-13.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:debb51b211d246f8326f6b6e982506a5d0d9906672c91bc478b66addc7ecc60a" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/52/c8/b2589d68acf7e3d63e2be330b84bc25712e97ed799affbca7edd7eae25d6/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e865447abfb83d6a98ad5130ed3c70b1fc295ae3eeee39fd07b4ddb0671b6788" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/1f/92/f899f7bbb5617bb65ec52a6eac1e9a1447a86b916c4194f8a5001b8cde0c/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46d8776a55d6d5da9dd6e9858fba2efcda2abe6743871dee47dd06eb8cb6d955" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/bb/a5/d7f01a415e134546248cef612adad8153c9f1eb10ec79505a7cd8294370b/cuda_bindings-13.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:45815daeb595bf3b405c52671a2542b1f8e9329f3b029494acbfcc74aeaa1f2d" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/df/93/eef988860a3ca985f82c4f3174fc0cdd94e07331ba9a92e8e064c260337f/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6629ca2df6f795b784752409bcaedbd22a7a651b74b56a165ebc0c9dcbd504d0" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/18/23/6db3aba46864aee357ab2415135b3fe3da7e9f1fa0221fa2a86a5968099c/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7dca0da053d3b4cc4869eff49c61c03f3c5dbaa0bcd712317a358d5b8f3f385d" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/c4/84/d3b6220b51cbc02ca14db7387e97445126b4ff5125aaa6c5dd7dcb75e679/cuda_bindings-13.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:8cebe3ce4aeeca5af9c490e175f76c4b569bbf4a35a62294b777bc77bf7ac4d8" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/c0/87/87a014f045b77c6de5c8527b0757fe644417b184e5367db977236a141602/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6464b30f46692d6c7f65d4a0e0450d81dd29de3afc1bb515653973d01c2cd6e" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/ee/5e/c0fe77a73aaefd3fff25ffaccaac69c5a63eafdf8b9a4c476626ef0ac703/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4af9f3e1be603fa12d5ad6cfca7844c9d230befa9792b5abdf7dd79979c3626" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/e3/73/98bcb069778fe420226db75aff54b5dd6c3ecfd0912edabab723326e80b7/cuda_bindings-13.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:bd658bb5c0e55b7b3e5dd0ed509c6addb298c665db26a9bfba35e1e626000ba2" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/5f/58/ed2c3b39c8dd5f96aa7a4abef0d47a73932c7a988e30f5fa428f00ed0da1/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df850a1ff8ce1b3385257b08e47b70e959932f5f432d0a4e46a355962b4e4771" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/1f/01/0c941b112ceeb21439b05895eace78ca1aa2eaaf695c8521a068fd9b4c00/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8a16384c6494e5485f39314b0b4afb04bee48d49edb16d5d8593fd35bbd231b" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/52/49/4e01cc06447d39476e138d1b1adec8d35c0d04eccd2c8d69befc08cd66e8/cuda_bindings-13.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6ccf14e0c1def3b7200100aafff3a9f7e210ecb6e409329e92dcf6cd2c00d5c7" }, +] + [[package]] name = "cuda-pathfinder" version = "1.5.3" @@ -602,6 +666,18 @@ wheels = [ name = "cuda-toolkit" version = "12.8.1" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +resolution-markers = [ + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.11' and platform_machine != 's390x'", + "python_full_version < '3.11' and platform_machine == 's390x'", +] wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/d4/c8/7dce3a0b15b42a3b58e7d96eb22a687d3bf2c44e01d149a6874629cd9938/cuda_toolkit-12.8.1-py2.py3-none-any.whl", hash = "sha256:adc7906af4ecbf9a352f9dca5734eceb21daec281ccfcf5675e1d2f724fc2cba" }, ] @@ -641,6 +717,61 @@ nvtx = [ { name = "nvidia-nvtx-cu12", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, ] +[[package]] +name = "cuda-toolkit" +version = "13.0.2" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +resolution-markers = [ + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.11' and platform_machine != 's390x'", + "python_full_version < '3.11' and platform_machine == 's390x'", +] +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/57/b2/453099f5f3b698d7d0eab38916aac44c7f76229f451709e2eb9db6615dcd/cuda_toolkit-13.0.2-py2.py3-none-any.whl", hash = "sha256:b198824cf2f54003f50d64ada3a0f184b42ca0846c1c94192fa269ecd97a66eb" }, +] + +[package.optional-dependencies] +cublas = [ + { name = "nvidia-cublas", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +cudart = [ + { name = "nvidia-cuda-runtime", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +cufft = [ + { name = "nvidia-cufft", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +cufile = [ + { name = "nvidia-cufile", marker = "sys_platform == 'linux'" }, +] +cupti = [ + { name = "nvidia-cuda-cupti", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +curand = [ + { name = "nvidia-curand", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +cusolver = [ + { name = "nvidia-cusolver", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +cusparse = [ + { name = "nvidia-cusparse", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +nvjitlink = [ + { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +nvrtc = [ + { name = "nvidia-cuda-nvrtc", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +nvtx = [ + { name = "nvidia-nvtx", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] + [[package]] name = "cycler" version = "0.12.1" @@ -901,8 +1032,8 @@ version = "5.10.2" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ { name = "maaagentbinary" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "strenum" }, ] wheels = [ @@ -919,7 +1050,7 @@ name = "macholib" version = "1.16.4" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ - { name = "altgraph", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu') or sys_platform == 'darwin' or (sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu') or (sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "altgraph", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu') or sys_platform == 'darwin' or (sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu') or (sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/10/2f/97589876ea967487978071c9042518d28b958d87b17dceb7cdc1d881f963/macholib-1.16.4.tar.gz", hash = "sha256:f408c93ab2e995cd2c46e34fe328b130404be143469e41bc366c807448979362" } wheels = [ @@ -1016,13 +1147,13 @@ name = "matplotlib" version = "3.10.9" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ - { name = "contourpy", version = "1.3.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "contourpy", version = "1.3.3", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "contourpy", version = "1.3.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "contourpy", version = "1.3.3", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "cycler" }, { name = "fonttools" }, { name = "kiwisolver" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "packaging" }, { name = "pillow" }, { name = "pyparsing" }, @@ -1091,8 +1222,8 @@ name = "ml-dtypes" version = "0.5.4" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/0e/4a/c27b42ed9b1c7d13d9ba8b6905dece787d6259152f2309338aed29b2447b/ml_dtypes-0.5.4.tar.gz", hash = "sha256:8ab06a50fb9bf9666dd0fe5dfb4676fa2b0ac0f31ecff72a6c3af8e22c063453" } wheels = [ @@ -1146,12 +1277,14 @@ name = "networkx" version = "3.4.2" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1" } wheels = [ @@ -1163,62 +1296,86 @@ name = "networkx" version = "3.6.1" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509" } wheels = [ @@ -1230,12 +1387,12 @@ name = "numpy" version = "2.2.6" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] @@ -1302,62 +1459,62 @@ name = "numpy" version = "2.4.4" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", @@ -1458,6 +1615,16 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/04/74/f4c001f4714c3ad9ce037e18cf2b9c64871a84951eaa0baf683a9ca9301c/numpy-2.4.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f2cf083b324a467e1ab358c105f6cad5ea950f50524668a80c486ff1db24e119" }, ] +[[package]] +name = "nvidia-cublas" +version = "13.1.0.3" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/e1/a5/fce49e2ae977e0ccc084e5adafceb4f0ac0c8333cb6863501618a7277f67/nvidia_cublas-13.1.0.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:c86fc7f7ae36d7528288c5d88098edcb7b02c633d262e7ddbb86b0ad91be5df2" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/e7/44/423ac00af4dd95a5aeb27207e2c0d9b7118702149bf4704c3ddb55bb7429/nvidia_cublas-13.1.0.3-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:ee8722c1f0145ab246bccb9e452153b5e0515fd094c3678df50b2a0888b8b171" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/10/f5/f50bc3f5c2bb57ab8f5b4d78bc1146b57810d42cb8fcb28cbe2e14050376/nvidia_cublas-13.1.0.3-py3-none-win_amd64.whl", hash = "sha256:2a3b94a37def342471c59fad7856caee4926809a72dd5270155d6a31b5b277be" }, +] + [[package]] name = "nvidia-cublas-cu12" version = "12.8.4.1" @@ -1468,6 +1635,16 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/70/61/7d7b3c70186fb651d0fbd35b01dbfc8e755f69fd58f817f3d0f642df20c3/nvidia_cublas_cu12-12.8.4.1-py3-none-win_amd64.whl", hash = "sha256:47e9b82132fa8d2b4944e708049229601448aaad7e6f296f630f2d1a32de35af" }, ] +[[package]] +name = "nvidia-cuda-cupti" +version = "13.0.85" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/2a/2a/80353b103fc20ce05ef51e928daed4b6015db4aaa9162ed0997090fe2250/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_aarch64.whl", hash = "sha256:796bd679890ee55fb14a94629b698b6db54bcfd833d391d5e94017dd9d7d3151" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/33/6d/737d164b4837a9bbd202f5ae3078975f0525a55730fe871d8ed4e3b952b0/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_x86_64.whl", hash = "sha256:4eb01c08e859bf924d222250d2e8f8b8ff6d3db4721288cf35d14252a4d933c8" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/ad/df/b74b10025c1205695c5676373f2edd3e87a7202cc62ead0dfbc373b0f6ea/nvidia_cuda_cupti-13.0.85-py3-none-win_amd64.whl", hash = "sha256:683f58d301548deeefcb8f6fac1b8d907691b9d8b18eccab417f51e362102f00" }, +] + [[package]] name = "nvidia-cuda-cupti-cu12" version = "12.8.90" @@ -1478,6 +1655,16 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/41/bc/83f5426095d93694ae39fe1311431b5d5a9bb82e48bf0dd8e19be2765942/nvidia_cuda_cupti_cu12-12.8.90-py3-none-win_amd64.whl", hash = "sha256:bb479dcdf7e6d4f8b0b01b115260399bf34154a1a2e9fe11c85c517d87efd98e" }, ] +[[package]] +name = "nvidia-cuda-nvrtc" +version = "13.0.88" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/c3/68/483a78f5e8f31b08fb1bb671559968c0ca3a065ac7acabfc7cee55214fd6/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:ad9b6d2ead2435f11cbb6868809d2adeeee302e9bb94bcf0539c7a40d80e8575" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/b7/dc/6bb80850e0b7edd6588d560758f17e0550893a1feaf436807d64d2da040f/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d27f20a0ca67a4bb34268a5e951033496c5b74870b868bacd046b1b8e0c3267b" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/4a/af/345fedb9f4c76c84ab4fa445b36bd4048a4d9db60e6bc76b4f913ff4b852/nvidia_cuda_nvrtc-13.0.88-py3-none-win_amd64.whl", hash = "sha256:6bcd4e7f8e205cbe644f5a98f2f799bef9556fefc89dd786e79a16312ce49872" }, +] + [[package]] name = "nvidia-cuda-nvrtc-cu12" version = "12.8.93" @@ -1488,6 +1675,16 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/45/51/52a3d84baa2136cc8df15500ad731d74d3a1114d4c123e043cb608d4a32b/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-win_amd64.whl", hash = "sha256:7a4b6b2904850fe78e0bd179c4b655c404d4bb799ef03ddc60804247099ae909" }, ] +[[package]] +name = "nvidia-cuda-runtime" +version = "13.0.96" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/87/4f/17d7b9b8e285199c58ce28e31b5c5bbaa4d8271af06a89b6405258245de2/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ef9bcbe90493a2b9d810e43d249adb3d02e98dd30200d86607d8d02687c43f55" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/2e/24/d1558f3b68b1d26e706813b1d10aa1d785e4698c425af8db8edc3dced472/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f82250d7782aa23b6cfe765ecc7db554bd3c2870c43f3d1821f1d18aebf0548" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/b7/94/6b867483bec07da24ffa32736c79fabb94ef3a7af4d787a9d4a974868576/nvidia_cuda_runtime-13.0.96-py3-none-win_amd64.whl", hash = "sha256:f79298c8a098cec150a597c8eba58ecdab96e3bdc4b9bc4f9983635031740492" }, +] + [[package]] name = "nvidia-cuda-runtime-cu12" version = "12.8.90" @@ -1511,6 +1708,32 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/a7/a5/48f07449fc9c6cc146dcafe6149fa5d69630137d2ec5b7d9e09f255fadd7/nvidia_cudnn_cu12-9.19.0.56-py3-none-win_amd64.whl", hash = "sha256:cec70596b9ce878fab83810c3f5a2e606d35f510e5fee579759e4cbc68a23750" }, ] +[[package]] +name = "nvidia-cudnn-cu13" +version = "9.19.0.56" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +dependencies = [ + { name = "nvidia-cublas", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, +] +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/f1/84/26025437c1e6b61a707442184fa0c03d083b661adf3a3eecfd6d21677740/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:6ed29ffaee1176c612daf442e4dd6cfeb6a0caa43ddcbeb59da94953030b1be4" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/a3/22/0b4b932655d17a6da1b92fa92ab12844b053bb2ac2475e179ba6f043da1e/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:d20e1734305e9d68889a96e3f35094d733ff1f83932ebe462753973e53a572bf" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/91/a2/f020386683ee9ab2c9a9f7f79290d9b0d07f7241de54dc746af2abd188d2/nvidia_cudnn_cu13-9.19.0.56-py3-none-win_amd64.whl", hash = "sha256:40d8c375005bcb01495f8edf375230b203a411a0c05fb6dc92a3781edcb23eac" }, +] + +[[package]] +name = "nvidia-cufft" +version = "12.0.0.61" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +dependencies = [ + { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, +] +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/a8/2f/7b57e29836ea8714f81e9898409196f47d772d5ddedddf1592eadb8ab743/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6c44f692dce8fd5ffd3e3df134b6cdb9c2f72d99cf40b62c32dde45eea9ddad3" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/85/b2/f8af21a2ed1beed337a6a02c5a28aeb85441f4d578ec3d529543c775ea4b/nvidia_cufft-12.0.0.61-py3-none-win_amd64.whl", hash = "sha256:2abce5b39d2f5ae12730fb7e5db6696533e36c26e2d3e8fd1750bdd2853364eb" }, +] + [[package]] name = "nvidia-cufft-cu12" version = "11.3.3.83" @@ -1524,6 +1747,15 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/7d/ec/ce1629f1e478bb5ccd208986b5f9e0316a78538dd6ab1d0484f012f8e2a1/nvidia_cufft_cu12-11.3.3.83-py3-none-win_amd64.whl", hash = "sha256:7a64a98ef2a7c47f905aaf8931b69a3a43f27c55530c698bb2ed7c75c0b42cb7" }, ] +[[package]] +name = "nvidia-cufile" +version = "1.15.1.6" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/3f/70/4f193de89a48b71714e74602ee14d04e4019ad36a5a9f20c425776e72cd6/nvidia_cufile-1.15.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08a3ecefae5a01c7f5117351c64f17c7c62efa5fffdbe24fc7d298da19cd0b44" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/ab/73/cc4a14c9813a8a0d509417cf5f4bdaba76e924d58beb9864f5a7baceefbf/nvidia_cufile-1.15.1.6-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:bdc0deedc61f548bddf7733bdc216456c2fdb101d020e1ab4b88d232d5e2f6d1" }, +] + [[package]] name = "nvidia-cufile-cu12" version = "1.13.1.3" @@ -1533,6 +1765,16 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/1e/f5/5607710447a6fe9fd9b3283956fceeee8a06cda1d2f56ce31371f595db2a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:4beb6d4cce47c1a0f1013d72e02b0994730359e17801d395bdcbf20cfb3bb00a" }, ] +[[package]] +name = "nvidia-curand" +version = "10.4.0.35" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/1e/72/7c2ae24fb6b63a32e6ae5d241cc65263ea18d08802aaae087d9f013335a2/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:133df5a7509c3e292aaa2b477afd0194f06ce4ea24d714d616ff36439cee349a" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/a5/9f/be0a41ca4a4917abf5cb9ae0daff1a6060cc5de950aec0396de9f3b52bc5/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:1aee33a5da6e1db083fe2b90082def8915f30f3248d5896bcec36a579d941bfc" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/99/27/72103153b1ffc00e09fdc40ac970235343dcd1ea8bd762e84d2d73219ffa/nvidia_curand-10.4.0.35-py3-none-win_amd64.whl", hash = "sha256:65b1710aa6961d326b411e314b374290904c5ddf41dc3f766ebc3f1d7d4ca69f" }, +] + [[package]] name = "nvidia-curand-cu12" version = "10.3.9.90" @@ -1543,6 +1785,21 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/b9/75/70c05b2f3ed5be3bb30b7102b6eb78e100da4bbf6944fd6725c012831cab/nvidia_curand_cu12-10.3.9.90-py3-none-win_amd64.whl", hash = "sha256:f149a8ca457277da854f89cf282d6ef43176861926c7ac85b2a0fbd237c587ec" }, ] +[[package]] +name = "nvidia-cusolver" +version = "12.0.4.66" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +dependencies = [ + { name = "nvidia-cublas", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "nvidia-cusparse", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, +] +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/5f/67/cba3777620cdacb99102da4042883709c41c709f4b6323c10781a9c3aa34/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0a759da5dea5c0ea10fd307de75cdeb59e7ea4fcb8add0924859b944babf1112" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/99/ef/332a0101260ca78a1daef046bf0b06199e8ed4dac1d2aa698289c358169c/nvidia_cusolver-12.0.4.66-py3-none-win_amd64.whl", hash = "sha256:16515bd33a8e76bb54d024cfa068fa68d30e80fc34b9e1090813ea9362e0cb65" }, +] + [[package]] name = "nvidia-cusolver-cu12" version = "11.7.3.90" @@ -1558,6 +1815,19 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/13/c0/76ca8551b8a84146ffa189fec81c26d04adba4bc0dbe09cd6e6fd9b7de04/nvidia_cusolver_cu12-11.7.3.90-py3-none-win_amd64.whl", hash = "sha256:4a550db115fcabc4d495eb7d39ac8b58d4ab5d8e63274d3754df1c0ad6a22d34" }, ] +[[package]] +name = "nvidia-cusparse" +version = "12.6.3.3" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +dependencies = [ + { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, +] +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/fa/18/623c77619c31d62efd55302939756966f3ecc8d724a14dab2b75f1508850/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b3c89c88d01ee0e477cb7f82ef60a11a4bcd57b6b87c33f789350b59759360b" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/02/b0/b043d6f3480f102f885cf87fc3ffd3edcb5e23b855025a50e2ef4d059185/nvidia_cusparse-12.6.3.3-py3-none-win_amd64.whl", hash = "sha256:cbcf42feb737bd7ec15b4c0a63e62351886bd3f975027b8815d7f720a2b5ea79" }, +] + [[package]] name = "nvidia-cusparse-cu12" version = "12.5.8.93" @@ -1581,6 +1851,16 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/2f/d8/a6b0d0d0c2435e9310f3e2bb0d9c9dd4c33daef86aa5f30b3681defd37ea/nvidia_cusparselt_cu12-0.7.1-py3-none-win_amd64.whl", hash = "sha256:f67fbb5831940ec829c9117b7f33807db9f9678dc2a617fbe781cac17b4e1075" }, ] +[[package]] +name = "nvidia-cusparselt-cu13" +version = "0.8.0" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/46/10/8dcd1175260706a2fc92a16a52e306b71d4c1ea0b0cc4a9484183399818a/nvidia_cusparselt_cu13-0.8.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:400c6ed1cf6780fc6efedd64ec9f1345871767e6a1a0a552a1ea0578117ea77c" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/fd/53/43b0d71f4e702fa9733f8b4571fdca50a8813f1e450b656c239beff12315/nvidia_cusparselt_cu13-0.8.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:25e30a8a7323935d4ad0340b95a0b69926eee755767e8e0b1cf8dd85b197d3fd" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/57/de/8f0578928b9b1246d7b1324db0528e6b9f9fb54496a49f40bf71f09f1a27/nvidia_cusparselt_cu13-0.8.0-py3-none-win_amd64.whl", hash = "sha256:e80212ed7b1afc97102fbb2b5c82487aa73f6a0edfa6d26c5a152593e520bb8f" }, +] + [[package]] name = "nvidia-nccl-cu12" version = "2.28.9" @@ -1590,6 +1870,25 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/4a/4e/44dbb46b3d1b0ec61afda8e84837870f2f9ace33c564317d59b70bc19d3e/nvidia_nccl_cu12-2.28.9-py3-none-manylinux_2_18_x86_64.whl", hash = "sha256:485776daa8447da5da39681af455aa3b2c2586ddcf4af8772495e7c532c7e5ab" }, ] +[[package]] +name = "nvidia-nccl-cu13" +version = "2.28.9" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/39/55/1920646a2e43ffd4fc958536b276197ed740e9e0c54105b4bb3521591fc7/nvidia_nccl_cu13-2.28.9-py3-none-manylinux_2_18_aarch64.whl", hash = "sha256:01c873ba1626b54caa12272ed228dc5b2781545e0ae8ba3f432a8ef1c6d78643" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/b0/b4/878fefaad5b2bcc6fcf8d474a25e3e3774bc5133e4b58adff4d0bca238bc/nvidia_nccl_cu13-2.28.9-py3-none-manylinux_2_18_x86_64.whl", hash = "sha256:e4553a30f34195f3fa1da02a6da3d6337d28f2003943aa0a3d247bbc25fefc42" }, +] + +[[package]] +name = "nvidia-nvjitlink" +version = "13.0.88" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/56/7a/123e033aaff487c77107195fa5a2b8686795ca537935a24efae476c41f05/nvidia_nvjitlink-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:13a74f429e23b921c1109976abefacc69835f2f433ebd323d3946e11d804e47b" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/ab/2c/93c5250e64df4f894f1cbb397c6fd71f79813f9fd79d7cd61de3f97b3c2d/nvidia_nvjitlink-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e931536ccc7d467a98ba1d8b89ff7fa7f1fa3b13f2b0069118cd7f47bff07d0c" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/e4/01/07530b0e37546231052e30234540289c42eaffa486f1a34a87fed340157b/nvidia_nvjitlink-13.0.88-py3-none-win_amd64.whl", hash = "sha256:634e96e3da9ef845ae744097a1f289238ecf946ce0b82e93cdce14b9782e682f" }, +] + [[package]] name = "nvidia-nvjitlink-cu12" version = "12.8.93" @@ -1609,6 +1908,25 @@ wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/b5/09/6ea3ea725f82e1e76684f0708bbedd871fc96da89945adeba65c3835a64c/nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:042f2500f24c021db8a06c5eec2539027d57460e1c1a762055a6554f72c369bd" }, ] +[[package]] +name = "nvidia-nvshmem-cu13" +version = "3.4.5" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/dc/0f/05cc9c720236dcd2db9c1ab97fff629e96821be2e63103569da0c9b72f19/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dc2a197f38e5d0376ad52cd1a2a3617d3cdc150fd5966f4aee9bcebb1d68fe9" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/3c/35/a9bf80a609e74e3b000fef598933235c908fcefcef9026042b8e6dfde2a9/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:290f0a2ee94c9f3687a02502f3b9299a9f9fe826e6d0287ee18482e78d495b80" }, +] + +[[package]] +name = "nvidia-nvtx" +version = "13.0.85" +source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } +wheels = [ + { url = "https://mirrors.cloud.tencent.com/pypi/packages/c2/f3/d86c845465a2723ad7e1e5c36dcd75ddb82898b3f53be47ebd429fb2fa5d/nvidia_nvtx-13.0.85-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4936d1d6780fbe68db454f5e72a42ff64d1fd6397df9f363ae786930fd5c1cd4" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/a8/64/3708a90d1ebe202ffdeb7185f878a3c84d15c2b2c31858da2ce0583e2def/nvidia_nvtx-13.0.85-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb7780edb6b14107373c835bf8b72e7a178bac7367e23da7acb108f973f157a6" }, + { url = "https://mirrors.cloud.tencent.com/pypi/packages/d2/50/0e2220f8620a177de994211186ffc5bfa9f2ce1e1282797f8f90096f9f88/nvidia_nvtx-13.0.85-py3-none-win_amd64.whl", hash = "sha256:d66ea44254dd3c6eacc300047af6e1288d2269dd072b417e0adffbf479e18519" }, +] + [[package]] name = "nvidia-nvtx-cu12" version = "12.8.90" @@ -1638,8 +1956,8 @@ version = "1.21.0" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ { name = "ml-dtypes" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "protobuf" }, { name = "typing-extensions" }, ] @@ -1680,8 +1998,8 @@ version = "0.2.1" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ { name = "ml-dtypes" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "onnx" }, { name = "sympy" }, { name = "typing-extensions" }, @@ -1696,21 +2014,21 @@ name = "onnxruntime" version = "1.24.3" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "flatbuffers", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "packaging", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "protobuf", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "sympy", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "flatbuffers", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "packaging", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "protobuf", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "sympy", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/15/41/3253db975a90c3ce1d475e2a230773a21cd7998537f0657947df6fb79861/onnxruntime-1.24.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3e6456801c66b095c5cd68e690ca25db970ea5202bd0c5b84a2c3ef7731c5a3c" }, @@ -1744,62 +2062,62 @@ name = "onnxruntime" version = "1.25.0" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", @@ -1826,10 +2144,10 @@ resolution-markers = [ "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "flatbuffers", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "packaging", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "protobuf", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "flatbuffers", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "packaging", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "protobuf", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/66/67/edb2cb6c4a38ebf539c3529c5449d68b8031f3006d8ee36574a6d45559b2/onnxruntime-1.25.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a71baa8e0e2f3417106e3a8b2183fd5741875b998041f1a2422a1d0240f302cb" }, @@ -1864,8 +2182,8 @@ version = "0.7.0" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ { name = "ml-dtypes" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "onnx" }, { name = "onnx-ir" }, { name = "packaging" }, @@ -1881,8 +2199,8 @@ name = "opencv-python" version = "4.13.0.92" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] wheels = [ { url = "https://mirrors.cloud.tencent.com/pypi/packages/fc/6f/5a28fef4c4a382be06afe3938c64cc168223016fa520c5abaf37e8862aa5/opencv_python-4.13.0.92-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:caf60c071ec391ba51ed00a4a920f996d0b64e3e46068aac1f646b5de0326a19" }, @@ -1909,20 +2227,20 @@ name = "pandas" version = "2.3.3" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "python-dateutil", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "pytz", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "tzdata", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "python-dateutil", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "pytz", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "tzdata", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b" } wheels = [ @@ -1980,62 +2298,62 @@ name = "pandas" version = "3.0.2" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", @@ -2062,9 +2380,9 @@ resolution-markers = [ "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "python-dateutil", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "tzdata", marker = "(python_full_version >= '3.11' and sys_platform == 'emscripten') or (python_full_version >= '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "python-dateutil", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "tzdata", marker = "(python_full_version >= '3.11' and sys_platform == 'emscripten') or (python_full_version >= '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/da/99/b342345300f13440fe9fe385c3c481e2d9a595ee3bab4d3219247ac94e9a/pandas-3.0.2.tar.gz", hash = "sha256:f4753e73e34c8d83221ba58f232433fca2748be8b18dbca02d242ed153945043" } wheels = [ @@ -2288,11 +2606,11 @@ version = "6.20.0" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ { name = "altgraph" }, - { name = "macholib", marker = "sys_platform == 'darwin' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "macholib", marker = "sys_platform == 'darwin' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "packaging" }, - { name = "pefile", marker = "sys_platform == 'win32' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "pefile", marker = "sys_platform == 'win32' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "pyinstaller-hooks-contrib" }, - { name = "pywin32-ctypes", marker = "sys_platform == 'win32' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "pywin32-ctypes", marker = "sys_platform == 'win32' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "setuptools" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/46/60/d03d52e6690d4e9caf333dcd14550cde634ce6c118b3bc8fa3112c3186fd/pyinstaller-6.20.0.tar.gz", hash = "sha256:95c5c7e03d5d61e9dfb8ef259c699cf492bb1041beb6dbe83696608cec07347a" } @@ -2516,8 +2834,8 @@ version = "3.8.1" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ { name = "colorlog" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "omegaconf" }, { name = "opencv-python" }, { name = "pillow" }, @@ -2552,20 +2870,20 @@ name = "scikit-learn" version = "1.7.2" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "joblib", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "threadpoolctl", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "joblib", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "threadpoolctl", marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda" } wheels = [ @@ -2606,62 +2924,62 @@ name = "scikit-learn" version = "1.8.0" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", @@ -2688,10 +3006,10 @@ resolution-markers = [ "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "joblib", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "scipy", version = "1.17.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "threadpoolctl", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "joblib", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "scipy", version = "1.17.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "threadpoolctl", marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd" } wheels = [ @@ -2738,17 +3056,17 @@ name = "scipy" version = "1.15.3" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", "python_full_version < '3.11' and platform_machine != 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version < '3.11' and platform_machine == 's390x' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf" } wheels = [ @@ -2804,62 +3122,62 @@ name = "scipy" version = "1.17.1" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128' and extra != 'extra-19-cannotmax-greenvine-cu130'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", @@ -2886,7 +3204,7 @@ resolution-markers = [ "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128'", ] dependencies = [ - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0" } wheels = [ @@ -2966,8 +3284,8 @@ name = "shapely" version = "2.1.2" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/4d/bc/0989043118a27cccb4e906a46b7565ce36ca7b57f5a18b78f4f1b0f72d9d/shapely-2.1.2.tar.gz", hash = "sha256:2ed4ecb28320a433db18a5bf029986aa8afcfd740745e78847e330d5d94922a9" } wheels = [ @@ -3091,16 +3409,17 @@ resolution-markers = [ "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin'", "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin'", "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin'", ] dependencies = [ - { name = "filelock", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (platform_machine != 's390x' and sys_platform == 'darwin')" }, - { name = "fsspec", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (platform_machine != 's390x' and sys_platform == 'darwin')" }, - { name = "jinja2", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (platform_machine != 's390x' and sys_platform == 'darwin')" }, - { name = "networkx", version = "3.4.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "networkx", version = "3.6.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "setuptools", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (platform_machine != 's390x' and sys_platform == 'darwin')" }, - { name = "sympy", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (platform_machine != 's390x' and sys_platform == 'darwin')" }, - { name = "typing-extensions", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (platform_machine != 's390x' and sys_platform == 'darwin')" }, + { name = "filelock", marker = "sys_platform == 'darwin'" }, + { name = "fsspec", marker = "sys_platform == 'darwin'" }, + { name = "jinja2", marker = "sys_platform == 'darwin'" }, + { name = "networkx", version = "3.4.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "networkx", version = "3.6.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "setuptools", marker = "sys_platform == 'darwin'" }, + { name = "sympy", marker = "sys_platform == 'darwin'" }, + { name = "typing-extensions", marker = "sys_platform == 'darwin'" }, ] wheels = [ { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91209c7d8a2460b76e8ff5b28b7623da4ab1d27474b79e1de83e954871985afe", upload-time = "2026-03-23T15:16:50Z" }, @@ -3143,17 +3462,16 @@ resolution-markers = [ "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin'", "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin'", ] dependencies = [ - { name = "filelock", marker = "(python_full_version < '3.11' and platform_machine == 's390x') or sys_platform != 'darwin'" }, - { name = "fsspec", marker = "(python_full_version < '3.11' and platform_machine == 's390x') or sys_platform != 'darwin'" }, - { name = "jinja2", marker = "(python_full_version < '3.11' and platform_machine == 's390x') or sys_platform != 'darwin'" }, - { name = "networkx", version = "3.4.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "networkx", version = "3.6.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "setuptools", marker = "(python_full_version < '3.11' and platform_machine == 's390x') or sys_platform != 'darwin'" }, - { name = "sympy", marker = "(python_full_version < '3.11' and platform_machine == 's390x') or sys_platform != 'darwin'" }, - { name = "typing-extensions", marker = "(python_full_version < '3.11' and platform_machine == 's390x') or sys_platform != 'darwin'" }, + { name = "filelock", marker = "sys_platform != 'darwin'" }, + { name = "fsspec", marker = "sys_platform != 'darwin'" }, + { name = "jinja2", marker = "sys_platform != 'darwin'" }, + { name = "networkx", version = "3.4.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "networkx", version = "3.6.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "setuptools", marker = "sys_platform != 'darwin'" }, + { name = "sympy", marker = "sys_platform != 'darwin'" }, + { name = "typing-extensions", marker = "sys_platform != 'darwin'" }, ] wheels = [ { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.11.0%2Bcpu-cp310-cp310-linux_s390x.whl", hash = "sha256:3d8a7789e61dbf11f8922672c43354614b9b0debd40899c0a94f1ad9e0bd6bd9", upload-time = "2026-04-27T21:55:13Z" }, @@ -3219,13 +3537,13 @@ resolution-markers = [ "python_full_version < '3.11' and platform_machine == 's390x'", ] dependencies = [ - { name = "cuda-bindings", marker = "sys_platform == 'linux'" }, - { name = "cuda-toolkit", extra = ["cublas", "cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "(sys_platform == 'linux' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "cuda-bindings", version = "12.9.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "sys_platform == 'linux'" }, + { name = "cuda-toolkit", version = "12.8.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, extra = ["cublas", "cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "(sys_platform == 'linux' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "filelock" }, { name = "fsspec" }, { name = "jinja2" }, - { name = "networkx", version = "3.4.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "networkx", version = "3.6.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "networkx", version = "3.4.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "networkx", version = "3.6.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "nvidia-cudnn-cu12", marker = "sys_platform == 'linux'" }, { name = "nvidia-cusparselt-cu12", marker = "sys_platform == 'linux'" }, { name = "nvidia-nccl-cu12", marker = "sys_platform == 'linux'" }, @@ -3259,6 +3577,79 @@ wheels = [ { url = "https://download-r2.pytorch.org/whl/cu128/torch-2.11.0%2Bcu128-cp314-cp314t-win_amd64.whl", hash = "sha256:d86c125d720c2c368c53bd1a4ef062916d91fa965c10448c74c78b5d039faf2d", upload-time = "2026-04-27T18:01:14Z" }, ] +[[package]] +name = "torch" +version = "2.11.0+cu130" +source = { registry = "https://download.pytorch.org/whl/cu130" } +resolution-markers = [ + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.11' and platform_machine != 's390x'", + "python_full_version < '3.11' and platform_machine == 's390x'", +] +dependencies = [ + { name = "cuda-bindings", version = "13.2.0", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "sys_platform == 'linux'" }, + { name = "cuda-toolkit", version = "13.0.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, extra = ["cublas", "cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "(sys_platform == 'linux' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "filelock" }, + { name = "fsspec" }, + { name = "jinja2" }, + { name = "networkx", version = "3.4.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "networkx", version = "3.6.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "nvidia-cudnn-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nvshmem-cu13", marker = "sys_platform == 'linux'" }, + { name = "setuptools" }, + { name = "sympy" }, + { name = "triton", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:4af01fad0822353e766770ff2c7d6bdc2cbcc2ac7fcd6da93a9e3c6f3f932b21", upload-time = "2026-04-27T19:56:16Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:4c5be01584b7fee22d3c0d04062fd28026044acd07ffd0ee64cbd54b60e62d39", upload-time = "2026-04-27T19:56:40Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp310-cp310-win_amd64.whl", hash = "sha256:5b603a44f34816e18df254443a1fbfb4eef7d57128e5c7f6655f7fab45071f6e", upload-time = "2026-04-27T19:57:45Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:6304535e9e4cd1beeab449e407712602aa473a97e7b310dc5650ef50940bd94f", upload-time = "2026-04-27T19:58:56Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:225b22e0a4e36ea573d3a68796e6816a160616f67e8b8c55683a88bf7777f4cd", upload-time = "2026-04-27T19:59:21Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp311-cp311-win_amd64.whl", hash = "sha256:a1ff66c0ad21bf48c3187e84a08a6895d48e9bae435e27811b0b65f36bef4555", upload-time = "2026-04-27T20:00:28Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:252f237d417fac3ba59b1635815c1f035a8241f2af038f2c076ed430932d89f1", upload-time = "2026-04-27T20:01:46Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:96911323dcfcd42028c7e8edde7bdf25bb187753234e8775f0f3f112e86a22db", upload-time = "2026-04-27T20:02:14Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp312-cp312-win_amd64.whl", hash = "sha256:ef8beae16d781c3244ef28dc7bee6d8871c26bbde65d5bf66e902cb61972c4ab", upload-time = "2026-04-27T20:03:28Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:c3d60f79666b9101e3914a2e5dec2e81eac834e13cae0bcf59e94dc1a465f756", upload-time = "2026-04-27T20:04:49Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:554461b76f21211927c776056bcb0b00fb42972364794b686d768ebb0b586366", upload-time = "2026-04-27T20:05:21Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp313-cp313-win_amd64.whl", hash = "sha256:339801f2163698a53c7fb3c91883e7f44331d22c34d45acfbce4eff71f2332fa", upload-time = "2026-04-27T20:06:44Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:a33905bc3e093b25d2b019181cf834f7f7d4c562739e13dd36a798ecb2e411b0", upload-time = "2026-04-27T20:08:23Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:6fd10ed484eb695312ae829719888bb9f6c7f5e8503528e3e8ad1b98a45296c2", upload-time = "2026-04-27T20:08:56Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp313-cp313t-win_amd64.whl", hash = "sha256:21d2734fd02af45d19bb88c0ff2e86b238ce73f7bde6003ade7f1454ae299198", upload-time = "2026-04-27T20:10:20Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:efcdfe08ec2c9db28b50cc7329fed0c90bb74fa6fbce0f7eb12e20db2279a40f", upload-time = "2026-04-27T20:11:48Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:6ccc36928fd17c86011b46fb81bd2c85475f1fbf967dde758672d6a8d83a212a", upload-time = "2026-04-27T20:12:18Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp314-cp314-win_amd64.whl", hash = "sha256:d886f1c2f4406d7ad0c59f254ceb0a9c47a03e97a7c704b778a2066d752dde29", upload-time = "2026-04-27T20:13:41Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:bdb20f8b04e9fcaba2f354c3026667bebb74de8a92526b706aa735e2df334c24", upload-time = "2026-04-27T20:15:02Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:28f952cd4a927616ad9d77644a93237d1ca50bf30d0cf26962b9162d8a00ffa0", upload-time = "2026-04-27T20:15:30Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.11.0%2Bcu130-cp314-cp314t-win_amd64.whl", hash = "sha256:d0a857adc487f275bfc9e7cdc51d12940613ba18b6362da214e20e9e3871f817", upload-time = "2026-04-27T20:16:48Z" }, +] + [[package]] name = "torchvision" version = "0.26.0" @@ -3273,12 +3664,13 @@ resolution-markers = [ "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin'", "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin'", "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "pillow", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (platform_machine != 's390x' and sys_platform == 'darwin')" }, - { name = "torch", version = "2.11.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (platform_machine != 's390x' and sys_platform == 'darwin')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "pillow", marker = "sys_platform == 'darwin'" }, + { name = "torch", version = "2.11.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin'" }, ] wheels = [ { url = "https://download-r2.pytorch.org/whl/cpu/torchvision-0.26.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a06d4772a8e13e772906ed736cc53ec6639e5e60554f8e5fa6ca165aabebc464", upload-time = "2026-03-23T15:36:09Z" }, @@ -3321,13 +3713,12 @@ resolution-markers = [ "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin'", "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "pillow", marker = "(python_full_version < '3.11' and platform_machine == 's390x') or sys_platform != 'darwin'" }, - { name = "torch", version = "2.11.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(python_full_version < '3.11' and platform_machine == 's390x') or sys_platform != 'darwin'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "pillow", marker = "sys_platform != 'darwin'" }, + { name = "torch", version = "2.11.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform != 'darwin'" }, ] wheels = [ { url = "https://download-r2.pytorch.org/whl/cpu/torchvision-0.26.0%2Bcpu-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:97df9a8595dce256d2e6dd16bbcd1c68dd00eec712e37d4b6ec7985453ddc2aa", upload-time = "2026-03-23T15:36:09Z" }, @@ -3386,8 +3777,8 @@ resolution-markers = [ "python_full_version < '3.11' and platform_machine == 's390x'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "pillow" }, { name = "torch", version = "2.11.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" } }, ] @@ -3415,12 +3806,74 @@ wheels = [ { url = "https://download-r2.pytorch.org/whl/cu128/torchvision-0.26.0%2Bcu128-cp314-cp314t-win_amd64.whl", hash = "sha256:f160dc552a086244f7102c898f7be8ef46a41b36bce5ea80a4f2493cb30ca1fc", upload-time = "2026-04-09T23:21:41Z" }, ] +[[package]] +name = "torchvision" +version = "0.26.0+cu130" +source = { registry = "https://download.pytorch.org/whl/cu130" } +resolution-markers = [ + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.11' and platform_machine != 's390x'", + "python_full_version < '3.11' and platform_machine == 's390x'", +] +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and extra != 'extra-19-cannotmax-greenvine-cpu' and extra != 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "pillow" }, + { name = "torch", version = "2.11.0+cu130", source = { registry = "https://download.pytorch.org/whl/cu130" } }, +] +wheels = [ + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:3094bed175eee817f9fc61d1c8bdecb1d807c25f49517fbfe60f15d24135fcde", upload-time = "2026-03-23T15:36:25Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:7d820708732d2467caf2ef16c3ad60c8ff402bd05bd246b108ee080f3fbfdc6e", upload-time = "2026-03-23T15:36:25Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp310-cp310-win_amd64.whl", hash = "sha256:a410cf52350ff4a5e0a71cedaf03bdec973c7a22421f36163d887a251acf51d7", upload-time = "2026-03-23T15:36:25Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:31f87cd00c09e071980d6a4ce218289a73302ad6a7ce0b3b62a74a4081fc339d", upload-time = "2026-03-23T15:36:25Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:3b53e3b611561e03ac261d06cb3f38782120ad9e0b4cd9f01549799097c713a6", upload-time = "2026-03-23T15:36:25Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp311-cp311-win_amd64.whl", hash = "sha256:d019b9d02433515d59ad403d8a6f521da7844c030d6c8003eeec39e15e59f9fa", upload-time = "2026-04-09T23:21:52Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:e2b39db78be674ee4ce7e921f54b70e5c281594c9267d981c061684ed38df936", upload-time = "2026-03-23T15:36:26Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:0f030a9bd8ada1a31b7111ea1589c1ecb5fa0884fee700a203e731b4cf378a98", upload-time = "2026-03-23T15:36:26Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp312-cp312-win_amd64.whl", hash = "sha256:a3578f7c8e8a2724306c68c56873a1675fa7ce45471e18235c720a2ed242fe44", upload-time = "2026-04-09T23:21:53Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:3af2c699719cc0e2518bf317664200e5a987fb75a25b9b3bf3817a4796ddd64f", upload-time = "2026-03-23T15:36:26Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:441a98bed4fff1d54b8450499e377e1a605bec31f2ecb1a38a340f95dcc83897", upload-time = "2026-03-23T15:36:26Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp313-cp313-win_amd64.whl", hash = "sha256:64de855465d6de60583e776889fad9412480f9f9e04fdd8d17ae96fa93864e9a", upload-time = "2026-04-09T23:21:54Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:c3ac485da79552b4f579c525c826f7a63288b0d1cafc1201b16e1148bfdea69a", upload-time = "2026-03-23T15:36:26Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:110659ff38cd1d2ca0ac6e6a0f2c842fcb5fe739dfe65ff7456a12b2c4dce775", upload-time = "2026-03-23T15:36:26Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp313-cp313t-win_amd64.whl", hash = "sha256:a7e19c3ab5c6d8e3c9f8c6d427f6b8862dfb8227ea4a758ea7a709951daf2f0d", upload-time = "2026-04-09T23:21:55Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:13fe3dee74a9ee31b551b10a8b4113d9bc5212bb0572a07af88b34a5d25d9701", upload-time = "2026-03-23T15:36:26Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:8d6a83a639d14f7e84f6b838a17e26f9ce41cdbe3dfe0c29ef74b32eb398ba28", upload-time = "2026-03-23T15:36:26Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp314-cp314-win_amd64.whl", hash = "sha256:ab671ffe837aff470baad6af97133ea5a49f8ea2383832550e510a63caf711e4", upload-time = "2026-04-09T23:21:56Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:cd89effa98de436ec22ccbbd278cdadc0fdec8eb81a396150f50b321c2230866", upload-time = "2026-03-23T15:36:26Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:aacac4d990ac794f3abeca66cc26affb42fbeba9789e5c351183665bab4902d2", upload-time = "2026-03-23T15:36:26Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.26.0%2Bcu130-cp314-cp314t-win_amd64.whl", hash = "sha256:23b9666084c72d07fc715001880b648e6a410796d1925c10f054f1ee034f5cc7", upload-time = "2026-04-09T23:21:57Z" }, +] + [[package]] name = "tqdm" version = "4.67.3" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "colorama", marker = "sys_platform == 'win32' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb" } wheels = [ @@ -3480,8 +3933,8 @@ name = "windows-capture" version = "2.0.0" source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" } dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version < '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "python_full_version >= '3.11' or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, { name = "opencv-python" }, ] sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/7e/60/1acdf4a4bb625bf04e94afde8bdb60a06997e644c6be7befe413dc5215ed/windows_capture-2.0.0.tar.gz", hash = "sha256:f1d587c182afa06c060e36ba2bd4f216a8ed03f320f51d7b6d5c8a2759469956" } From 420145fa16e38f61a45cfbb68f40f986d5196cec Mon Sep 17 00:00:00 2001 From: Ancientea <1518101374@qq.com> Date: Fri, 1 May 2026 12:45:39 +0800 Subject: [PATCH 7/9] =?UTF-8?q?=E7=BB=BF=E8=97=A4=E5=9F=8Edlc=E5=9B=BE?= =?UTF-8?q?=E7=89=87=E9=80=82=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...5\351\207\217\344\272\247\345\236\213.png" | Bin 0 -> 28118 bytes ...4\346\236\234\350\205\271\342\200\235.png" | Bin 0 -> 40184 bytes .../\345\207\257\345\260\224\345\270\214.png" | Bin 0 -> 37240 bytes ...3\345\276\222\345\215\241\351\227\250.png" | Bin 0 -> 28785 bytes ...4\347\251\277\345\210\272\350\200\205.png" | Bin 0 -> 90143 bytes ...7\345\205\211\351\225\234\345\215\253.png" | Bin 0 -> 27788 bytes ...4\344\271\213\344\270\273\342\200\235.png" | Bin 0 -> 91989 bytes ...3\346\211\213\345\233\232\347\212\257.png" | Bin 31756 -> 110106 bytes ...0\351\243\237\350\231\253\350\212\261.png" | Bin 39975 -> 0 bytes ...3\346\261\241\350\272\257\345\243\263.png" | Bin 0 -> 93330 bytes ...2\351\205\222\345\212\251\346\211\213.png" | Bin 0 -> 87838 bytes ...0\351\274\267\345\212\233\345\243\253.png" | Bin 0 -> 102555 bytes ...4\346\211\213\345\233\232\347\212\257.png" | Bin 0 -> 103260 bytes .../\351\200\220\350\205\220\345\205\275.png" | Bin 0 -> 116557 bytes monster_greenvine.csv | 20 +++++++++--------- 15 files changed, 10 insertions(+), 10 deletions(-) create mode 100644 "images/\342\200\234\344\272\244\351\200\232\344\272\255\342\200\235\351\207\217\344\272\247\345\236\213.png" create mode 100644 "images/\342\200\234\346\236\234\350\205\271\342\200\235.png" create mode 100644 "images/\345\207\257\345\260\224\345\270\214.png" create mode 100644 "images/\345\234\243\345\276\222\345\215\241\351\227\250.png" create mode 100644 "images/\345\257\214\350\220\245\345\205\273\347\232\204\347\251\277\345\210\272\350\200\205.png" create mode 100644 "images/\345\274\247\345\205\211\351\225\234\345\215\253.png" create mode 100644 "images/\346\211\216\347\275\227\357\274\214\342\200\234\347\213\274\344\271\213\344\270\273\342\200\235.png" delete mode 100644 "images/\346\232\264\350\265\260\351\243\237\350\231\253\350\212\261.png" create mode 100644 "images/\346\237\223\346\261\241\350\272\257\345\243\263.png" create mode 100644 "images/\347\203\210\351\205\222\347\272\247\351\206\222\351\205\222\345\212\251\346\211\213.png" create mode 100644 "images/\347\224\260\351\274\267\345\212\233\345\243\253.png" create mode 100644 "images/\347\245\236\345\260\204\346\211\213\345\233\232\347\212\257.png" create mode 100644 "images/\351\200\220\350\205\220\345\205\275.png" diff --git "a/images/\342\200\234\344\272\244\351\200\232\344\272\255\342\200\235\351\207\217\344\272\247\345\236\213.png" "b/images/\342\200\234\344\272\244\351\200\232\344\272\255\342\200\235\351\207\217\344\272\247\345\236\213.png" new file mode 100644 index 0000000000000000000000000000000000000000..fe170e3fa8fcf6311c032845eb851c70e849cfe9 GIT binary patch literal 28118 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 literal 0 HcmV?d00001 diff --git "a/images/\345\234\243\345\276\222\345\215\241\351\227\250.png" "b/images/\345\234\243\345\276\222\345\215\241\351\227\250.png" new file mode 100644 index 0000000000000000000000000000000000000000..ab91b1e05f068b87fca2f4ce829a7d5aa6b7f120 GIT binary patch literal 28785 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 KMNUMnLSTZo*+=sL literal 0 HcmV?d00001 diff --git "a/images/\345\274\247\345\205\211\351\225\234\345\215\253.png" "b/images/\345\274\247\345\205\211\351\225\234\345\215\253.png" new file mode 100644 index 0000000000000000000000000000000000000000..01b76df4e34bc64527578e793c868f3ac6b020ea GIT binary patch literal 27788 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 literal 0 HcmV?d00001 diff --git "a/images/\346\211\216\347\275\227\357\274\214\342\200\234\347\213\274\344\271\213\344\270\273\342\200\235.png" "b/images/\346\211\216\347\275\227\357\274\214\342\200\234\347\213\274\344\271\213\344\270\273\342\200\235.png" new file mode 100644 index 0000000000000000000000000000000000000000..9fdf77cb7f10ae619274a9bf490684b6a807f62f GIT binary patch literal 91989 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⪙_)!+^NpgjBm!Ai`WsPi8u}ozIJUq6 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 literal 0 HcmV?d00001 diff --git "a/images/\346\213\263\346\211\213\345\233\232\347\212\257.png" "b/images/\346\213\263\346\211\213\345\233\232\347\212\257.png" index 9a5a9bdd936a8542899e8f3fb44b289d0e1e0a6f..72561fffd2af81000aff9e27a8554ba3e45004d2 100644 GIT binary patch literal 110106 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 y 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

- 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

62qCoG4q@d8*>*M44jA} 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 literal 0 HcmV?d00001 diff --git "a/images/\347\224\260\351\274\267\345\212\233\345\243\253.png" "b/images/\347\224\260\351\274\267\345\212\233\345\243\253.png" new file mode 100644 index 0000000000000000000000000000000000000000..d28885959e1302078509b50990170c1efebefeb9 GIT binary patch literal 102555 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

#Ecx6qcFU1>BwhAX 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 z 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 literal 0 HcmV?d00001 diff --git "a/images/\347\245\236\345\260\204\346\211\213\345\233\232\347\212\257.png" "b/images/\347\245\236\345\260\204\346\211\213\345\233\232\347\212\257.png" new file mode 100644 index 0000000000000000000000000000000000000000..14735ec83bc1c5d398e19d557a7cb023dd862ca7 GIT binary patch literal 103260 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

DcUw;)XgmoK@ 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 S*~@eQ0000 Date: Fri, 1 May 2026 13:13:15 +0800 Subject: [PATCH 8/9] Fix package button and update version number --- auto_fetch.py | 3 +- data_package.py | 2 +- input_panel_ui.py | 12 ++++---- main.py | 16 ++++++---- pyproject.toml | 2 +- uv.lock | 78 +++++++++++++++++++++++------------------------ 6 files changed, 59 insertions(+), 54 deletions(-) diff --git a/auto_fetch.py b/auto_fetch.py index 9c758ac..159cd4f 100644 --- a/auto_fetch.py +++ b/auto_fetch.py @@ -405,7 +405,7 @@ def save_statistics_to_log(self): stats_text = ( f"总共填写次数: {self.total_fill_count}\n" f"填写×次数: {self.incorrect_fill_count}\n" - f"当次运行时长: {int(hours)}小时{int(minutes)}分钟\n" + f"运行: {int(hours)}小时{int(minutes)}分钟\n" ) with open("log.txt", "a", encoding="utf-8") as log_file: log_file.write(stats_text) @@ -705,6 +705,7 @@ def auto_fetch_loop(self): # 检测一次间隔时间—————————————————————————————————— time.sleep(0.2) except Exception as e: + logger.exception(f"自动获取数据出错:\n{e}") self._log(logging.ERROR, f"自动获取数据出错:\n{e}") break diff --git a/data_package.py b/data_package.py index dbc90b5..951aaca 100644 --- a/data_package.py +++ b/data_package.py @@ -42,7 +42,7 @@ def create_zip_package(output_zip_path): def package_data(): # 使用当前时间生成输出文件名 - current_time = datetime.now().strftime("%Y_%m_%d__%H_%M_%S") + current_time = datetime.now().strftime("%Y%m%d_%H%M%S") output_zip = f"arknights_package_{current_time}.zip" # 调用函数创建压缩包 diff --git a/input_panel_ui.py b/input_panel_ui.py index 602ae10..353e9ed 100644 --- a/input_panel_ui.py +++ b/input_panel_ui.py @@ -156,7 +156,7 @@ def init_ui(self): self.scroll_grid.setContentsMargins(5, 5, 5, 5) # 设置5列布局 - self.COLUMNS = 7 + self.COLUMNS = 10 self.ROW_HEIGHT = 120 # 每个单元的高度 scroll.setWidget(scroll_content) @@ -342,15 +342,15 @@ def load_images(self): # 人物图片 img_label = QLabel() - img_label.setFixedSize(60, 60) + img_label.setFixedSize(50, 50) img_label.setAlignment(Qt.AlignmentFlag.AlignCenter) try: pixmap = QPixmap(f"images/{MONSTER_DATA['原始名称'][i]}.png") if not pixmap.isNull(): pixmap = pixmap.scaled( - 60, - 60, + 50, + 50, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation, ) @@ -368,7 +368,7 @@ def load_images(self): # 左输入框 left_entry = QLineEdit() - left_entry.setFixedWidth(60) + left_entry.setFixedWidth(50) left_entry.setPlaceholderText("左") left_entry.setAlignment(Qt.AlignmentFlag.AlignCenter) left_entry.textChanged.connect(self.input_changed.emit) # Connect to signal @@ -376,7 +376,7 @@ def load_images(self): # 右输入框 (放在左输入框下方) right_entry = QLineEdit() - right_entry.setFixedWidth(60) + right_entry.setFixedWidth(50) right_entry.setPlaceholderText("右") right_entry.setAlignment(Qt.AlignmentFlag.AlignCenter) right_entry.textChanged.connect(self.input_changed.emit) # Connect to signal diff --git a/main.py b/main.py index 85a35e4..1f55f00 100644 --- a/main.py +++ b/main.py @@ -74,6 +74,7 @@ class ArknightsApp(QMainWindow): update_monster_signal = pyqtSignal(list) update_prediction_signal = pyqtSignal(float) update_statistics_signal = pyqtSignal() # 用于更新统计信息 + update_package_button_signal = pyqtSignal(bool) # 用于更新打包按钮启用状态 qt_button_style = """ QPushButton { background-color: #313131; @@ -147,9 +148,9 @@ def init_ui(self): model_name = Path(self.cannot_model.model_path).name if self.cannot_model.model_path else "未加载" self.setWindowTitle(f"铁鲨鱼_Arknights Neural Network - v{version} - model: {model_name}") self.setWindowIcon(QIcon("ico/icon.ico")) - self.setGeometry(100, 100, 500, 580) - self.setMinimumWidth(580) - self.setMaximumWidth(580) + self.setGeometry(100, 100, 570, 570) + self.setMinimumWidth(570) + self.setMaximumWidth(570) self.background = QPixmap("ico/background.png") # 初始化动画对象 @@ -162,9 +163,9 @@ def init_ui(self): main_layout = QHBoxLayout(main_widget) main_layout.setAlignment(Qt.AlignmentFlag.AlignLeft) - # 左侧面板 + # 输入面板 self.input_panel = InputPanelUI() - self.input_panel.setFixedWidth(528) + self.input_panel.setFixedWidth(640) self.input_panel.predict_requested.connect(self.predict) self.input_panel.reset_requested.connect(self.reset_entries) self.input_panel.input_changed.connect(self.update_input_display) @@ -504,6 +505,7 @@ def init_ui(self): self.update_monster_signal.connect(self.update_monster) self.update_prediction_signal.connect(self.update_prediction) self.update_statistics_signal.connect(self.update_statistics) + self.update_package_button_signal.connect(self.package_data_button.setEnabled) self.refresh_device_list() DarkModeStyleFix.apply(QApplication.instance()) @@ -939,7 +941,7 @@ def update_statistics(self): stats_text = ( f"总共填写次数: {self.auto_fetch.total_fill_count}, " f"填写×次数: {self.auto_fetch.incorrect_fill_count}, " - f"当次运行时长: {int(hours)}小时{int(minutes)}分钟" + f"运行时长: {int(hours)}小时{int(minutes)}分钟" ) self.stats_label.setText(stats_text) @@ -967,9 +969,11 @@ def update_device_serial(self): def start_callback(self): self.update_button_signal.emit("停止自动获取数据") + self.update_package_button_signal.emit(False) def stop_callback(self): self.update_button_signal.emit("自动获取数据") + self.update_package_button_signal.emit(True) def update_monster_callback(self, results: list): self.update_monster_signal.emit(results) diff --git a/pyproject.toml b/pyproject.toml index 286692d..036b046 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "CannotMax-Greenvine" -version = "1.0.8" +version = "1.1.0" description = "这是一个基于深度学习的明日方舟游戏辅助工具,用于自动识别游戏画面中的单位并预测战斗结果。" readme = "README.md" requires-python = ">=3.10" diff --git a/uv.lock b/uv.lock index 2b69784..db4cc47 100644 --- a/uv.lock +++ b/uv.lock @@ -119,7 +119,7 @@ sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/3e/38/7859ff463 [[package]] name = "cannotmax-greenvine" -version = "1.0.8" +version = "1.1.0" source = { virtual = "." } dependencies = [ { name = "maafw" }, @@ -143,10 +143,10 @@ dependencies = [ [package.optional-dependencies] cpu = [ - { name = "torch", version = "2.11.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "torch", version = "2.11.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "torchvision", version = "0.26.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "torchvision", version = "0.26.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "torch", version = "2.11.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "torch", version = "2.11.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "torchvision", version = "0.26.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "torchvision", version = "0.26.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, ] cu128 = [ { name = "torch", version = "2.11.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" } }, @@ -3401,25 +3401,20 @@ version = "2.11.0" source = { registry = "https://download.pytorch.org/whl/cpu" } resolution-markers = [ "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin'", "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin'", "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin'", "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin'", "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin'", ] dependencies = [ - { name = "filelock", marker = "sys_platform == 'darwin'" }, - { name = "fsspec", marker = "sys_platform == 'darwin'" }, - { name = "jinja2", marker = "sys_platform == 'darwin'" }, - { name = "networkx", version = "3.4.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "networkx", version = "3.6.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "setuptools", marker = "sys_platform == 'darwin'" }, - { name = "sympy", marker = "sys_platform == 'darwin'" }, - { name = "typing-extensions", marker = "sys_platform == 'darwin'" }, + { name = "filelock", marker = "platform_machine != 's390x' and sys_platform == 'darwin'" }, + { name = "fsspec", marker = "platform_machine != 's390x' and sys_platform == 'darwin'" }, + { name = "jinja2", marker = "platform_machine != 's390x' and sys_platform == 'darwin'" }, + { name = "networkx", version = "3.4.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version >= '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "networkx", version = "3.6.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version < '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (python_full_version < '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "setuptools", marker = "platform_machine != 's390x' and sys_platform == 'darwin'" }, + { name = "sympy", marker = "platform_machine != 's390x' and sys_platform == 'darwin'" }, + { name = "typing-extensions", marker = "platform_machine != 's390x' and sys_platform == 'darwin'" }, ] wheels = [ { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91209c7d8a2460b76e8ff5b28b7623da4ab1d27474b79e1de83e954871985afe", upload-time = "2026-03-23T15:16:50Z" }, @@ -3442,6 +3437,7 @@ resolution-markers = [ "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin'", "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32'", "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32'", "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32'", @@ -3460,18 +3456,22 @@ resolution-markers = [ "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin'", "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin'", "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin'", ] dependencies = [ - { name = "filelock", marker = "sys_platform != 'darwin'" }, - { name = "fsspec", marker = "sys_platform != 'darwin'" }, - { name = "jinja2", marker = "sys_platform != 'darwin'" }, - { name = "networkx", version = "3.4.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "networkx", version = "3.6.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "setuptools", marker = "sys_platform != 'darwin'" }, - { name = "sympy", marker = "sys_platform != 'darwin'" }, - { name = "typing-extensions", marker = "sys_platform != 'darwin'" }, + { name = "filelock", marker = "platform_machine == 's390x' or sys_platform != 'darwin'" }, + { name = "fsspec", marker = "platform_machine == 's390x' or sys_platform != 'darwin'" }, + { name = "jinja2", marker = "platform_machine == 's390x' or sys_platform != 'darwin'" }, + { name = "networkx", version = "3.4.2", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "networkx", version = "3.6.1", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "setuptools", marker = "platform_machine == 's390x' or sys_platform != 'darwin'" }, + { name = "sympy", marker = "platform_machine == 's390x' or sys_platform != 'darwin'" }, + { name = "typing-extensions", marker = "platform_machine == 's390x' or sys_platform != 'darwin'" }, ] wheels = [ { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.11.0%2Bcpu-cp310-cp310-linux_s390x.whl", hash = "sha256:3d8a7789e61dbf11f8922672c43354614b9b0debd40899c0a94f1ad9e0bd6bd9", upload-time = "2026-04-27T21:55:13Z" }, @@ -3656,21 +3656,16 @@ version = "0.26.0" source = { registry = "https://download.pytorch.org/whl/cpu" } resolution-markers = [ "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'darwin'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin'", "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'darwin'", "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'darwin'", "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'darwin'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin'", - "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin'", - "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin'", "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin'", - "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "pillow", marker = "sys_platform == 'darwin'" }, - { name = "torch", version = "2.11.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version >= '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and platform_machine != 's390x' and sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version < '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (python_full_version < '3.11' and platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "pillow", marker = "platform_machine != 's390x' and sys_platform == 'darwin'" }, + { name = "torch", version = "2.11.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "platform_machine != 's390x' and sys_platform == 'darwin'" }, ] wheels = [ { url = "https://download-r2.pytorch.org/whl/cpu/torchvision-0.26.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a06d4772a8e13e772906ed736cc53ec6639e5e60554f8e5fa6ca165aabebc464", upload-time = "2026-03-23T15:36:09Z" }, @@ -3693,6 +3688,7 @@ resolution-markers = [ "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten'", "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'darwin'", "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32'", "python_full_version == '3.12.*' and platform_machine != 's390x' and sys_platform == 'win32'", "python_full_version == '3.11.*' and platform_machine != 's390x' and sys_platform == 'win32'", @@ -3711,14 +3707,18 @@ resolution-markers = [ "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'darwin'", + "python_full_version == '3.12.*' and platform_machine == 's390x' and sys_platform == 'darwin'", + "python_full_version == '3.11.*' and platform_machine == 's390x' and sys_platform == 'darwin'", "python_full_version < '3.11' and platform_machine != 's390x' and sys_platform != 'darwin'", "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform != 'darwin'", + "python_full_version < '3.11' and platform_machine == 's390x' and sys_platform == 'darwin'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform == 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, - { name = "pillow", marker = "sys_platform != 'darwin'" }, - { name = "torch", version = "2.11.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform != 'darwin'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version < '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version >= '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }, marker = "(python_full_version >= '3.11' and platform_machine == 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version >= '3.11' and sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (python_full_version < '3.11' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (platform_machine != 's390x' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu130') or (sys_platform != 'darwin' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130') or (extra != 'extra-19-cannotmax-greenvine-cpu' and extra == 'extra-19-cannotmax-greenvine-cu128' and extra == 'extra-19-cannotmax-greenvine-cu130')" }, + { name = "pillow", marker = "platform_machine == 's390x' or sys_platform != 'darwin'" }, + { name = "torch", version = "2.11.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "platform_machine == 's390x' or sys_platform != 'darwin'" }, ] wheels = [ { url = "https://download-r2.pytorch.org/whl/cpu/torchvision-0.26.0%2Bcpu-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:97df9a8595dce256d2e6dd16bbcd1c68dd00eec712e37d4b6ec7985453ddc2aa", upload-time = "2026-03-23T15:36:09Z" }, From cae200defddd2f3a98010efe048ea9b15aa7d441 Mon Sep 17 00:00:00 2001 From: 1ZUMIKun <68433151+1ZUMIKun@users.noreply.github.com> Date: Fri, 1 May 2026 13:33:44 +0800 Subject: [PATCH 9/9] Add disable predict option --- auto_fetch.py | 2 +- main.py | 36 +++++++++++++++++++++++++++--------- multi_instance.py | 26 ++++++++++++++++++++------ 3 files changed, 48 insertions(+), 16 deletions(-) diff --git a/auto_fetch.py b/auto_fetch.py index 159cd4f..32f1717 100644 --- a/auto_fetch.py +++ b/auto_fetch.py @@ -454,7 +454,7 @@ def recognize_and_predict(self, screenshot = None): else: logger.error("识别结果有错误,本轮跳过") # 选择预测方法 - if self.cannot_model.is_model_loaded: + if self.cannot_model and self.cannot_model.is_model_loaded: if self.field_recognizer is not None: # 构建包含地形的完整特征向量 full_features = self.build_terrain_features(left_counts, right_counts) diff --git a/main.py b/main.py index 1f55f00..ad80522 100644 --- a/main.py +++ b/main.py @@ -278,7 +278,7 @@ def init_ui(self): control_group = QGroupBox("控制面板") control_layout = QVBoxLayout(control_group) - # 第一行按钮 + # 第一行按钮 - 基础设置 row1 = QWidget() row1_layout = QHBoxLayout(row1) row1_layout.setContentsMargins(0, 0, 0, 0) @@ -287,21 +287,33 @@ def init_ui(self): self.duration_entry = QLineEdit("325") self.duration_entry.setFixedWidth(50) - self.auto_fetch_button = QPushButton("自动获取数据") - self.auto_fetch_button.clicked.connect(self.toggle_auto_fetch) - self.mode_menu = QComboBox() self.mode_menu.addItems(["单人", "30人"]) self.mode_menu.currentTextChanged.connect(self.update_game_mode) + self.predict_enabled_checkbox = QCheckBox("预测") + self.predict_enabled_checkbox.setChecked(True) + self.predict_enabled_checkbox.toggled.connect(self._on_predict_toggled) + self.invest_checkbox = QCheckBox("投资") self.invest_checkbox.stateChanged.connect(self.update_invest_status) row1_layout.addWidget(self.duration_label) row1_layout.addWidget(self.duration_entry) - row1_layout.addWidget(self.auto_fetch_button) row1_layout.addWidget(self.mode_menu) + row1_layout.addWidget(self.predict_enabled_checkbox) row1_layout.addWidget(self.invest_checkbox) + row1_layout.addStretch() + + # 第二行按钮 - 自动获取数据配置 + row_config = QWidget() + row_config_layout = QHBoxLayout(row_config) + row_config_layout.setContentsMargins(0, 0, 0, 0) + + self.auto_fetch_button = QPushButton("自动获取数据") + self.auto_fetch_button.clicked.connect(self.toggle_auto_fetch) + + row_config_layout.addWidget(self.auto_fetch_button) # 第二行按钮 - 数据操作和统计 row2 = QWidget() @@ -329,6 +341,7 @@ def init_ui(self): # 添加到控制布局 control_layout.addWidget(row1) + control_layout.addWidget(row_config) control_layout.addWidget(row2) control_layout.addWidget(github_label) @@ -928,7 +941,7 @@ def toggle_auto_fetch(self): stop_callback=self.stop_callback, training_duration=float(self.duration_entry.text()) * 3600, # 获取训练时长 recognizer=self.recognizer, - cannot_model=self.cannot_model, + cannot_model=self.cannot_model if self.predict_enabled_checkbox.isChecked() else None, ) self.auto_fetch.start_auto_fetch() else: @@ -939,9 +952,9 @@ def update_statistics(self): hours, remainder = divmod(elapsed_time, 3600) minutes, _ = divmod(remainder, 60) stats_text = ( - f"总共填写次数: {self.auto_fetch.total_fill_count}, " - f"填写×次数: {self.auto_fetch.incorrect_fill_count}, " - f"运行时长: {int(hours)}小时{int(minutes)}分钟" + f"总次数: {self.auto_fetch.total_fill_count}, " + f"填写×次数: {self.auto_fetch.incorrect_fill_count}, " + f"运行: {int(hours)}小时{int(minutes)}分钟" ) self.stats_label.setText(stats_text) @@ -1065,6 +1078,11 @@ def update_game_mode(self, mode): def update_invest_status(self, state): self.is_invest = state == Qt.CheckState.Checked.value + def _on_predict_toggled(self, checked): + self.invest_checkbox.setEnabled(checked) + if not checked: + self.invest_checkbox.setChecked(False) + def update_result(self, text): self.result_label.setText(text) diff --git a/multi_instance.py b/multi_instance.py index b9307c2..2ed65b2 100644 --- a/multi_instance.py +++ b/multi_instance.py @@ -166,12 +166,14 @@ def __init__(self, port): self.thread_running = False # 线程是否正在运行的标志 self.game_mode = None # 保存游戏模式 self.is_invest = None # 保存投资设置 + self.is_predict = None # 保存预测设置 - def start(self, game_mode, is_invest): + def start(self, game_mode, is_invest, is_predict): try: # 保存设置 self.game_mode = game_mode self.is_invest = is_invest + self.is_predict = is_predict # 重置停止事件和标志 self.stop_event.clear() @@ -179,7 +181,7 @@ def start(self, game_mode, is_invest): self.last_activity_time = time.time() self.status = "连接中" - logger.info(f"[{self.serial}] 开始启动实例,游戏模式: {game_mode}, 自动投资: {is_invest}") + logger.info(f"[{self.serial}] 开始启动实例,游戏模式: {game_mode}, 自动投资: {is_invest}, 预测: {is_predict}") # 记录实例启动时间戳 self.start_time = time.time() @@ -226,7 +228,7 @@ def start(self, game_mode, is_invest): stop_callback=self._on_stop_callback, training_duration=-1, recognizer=get_recognizer(), - cannot_model=get_cannot_model(), + cannot_model=get_cannot_model() if is_predict else None, field_recognizer=get_field_recognizer() if FIELD_FEATURE_COUNT > 0 else None, start_timestamp=self.start_time, # 传递实例启动时间戳 ) @@ -324,6 +326,11 @@ def init_ui(self): self.game_mode_combo.addItems(["单人", "30人"]) settings_layout.addWidget(QLabel("模式:")) settings_layout.addWidget(self.game_mode_combo) + + self.predict_check = QCheckBox("预测") + self.predict_check.setChecked(True) + self.predict_check.toggled.connect(self._on_predict_toggled) + settings_layout.addWidget(self.predict_check) self.invest_check = QCheckBox("自动投资") self.invest_check.setChecked(False) @@ -406,6 +413,11 @@ def _parse_ports(text): parts = text.replace('\n', ',').replace(',', ',').replace(';', ',').replace(' ', ',').split(',') return [p.strip() for p in parts if p.strip().isdigit()] + def _on_predict_toggled(self, checked): + self.invest_check.setEnabled(checked) + if not checked: + self.invest_check.setChecked(False) + def start_all(self): try: Path("multi_ports.txt").write_text(self.ports_input.text()) @@ -416,8 +428,9 @@ def start_all(self): ports = self._parse_ports(self.ports_input.text()) game_mode = self.game_mode_combo.currentText() is_invest = self.invest_check.isChecked() + is_predict = self.predict_check.isChecked() - logger.info(f"开始启动多开实例,端口列表: {ports}, 游戏模式: {game_mode}, 自动投资: {is_invest}") + logger.info(f"开始启动多开实例,端口列表: {ports}, 游戏模式: {game_mode}, 预测: {is_predict}, 自动投资: {is_invest}") def start_single_instance(port): # 检查是否已经在运行或正在启动中 @@ -440,7 +453,7 @@ def start_single_instance(port): # 立即添加到实例字典,以便 stop_all 可以找到它 self.instances[port] = instance try: - if instance.start(game_mode, is_invest): + if instance.start(game_mode, is_invest, is_predict): logger.info(f"端口 {port} 的实例启动成功") else: logger.error(f"端口 {port} 的实例启动失败") @@ -594,6 +607,7 @@ def _toggle_port(self, port): else: game_mode = self.game_mode_combo.currentText() is_invest = self.invest_check.isChecked() + is_predict = self.predict_check.isChecked() def do_start(): if port in self.starting_ports: @@ -602,7 +616,7 @@ def do_start(): instance = DeviceInstance(port) self.instances[port] = instance try: - instance.start(game_mode, is_invest) + instance.start(game_mode, is_invest, is_predict) finally: self.starting_ports.discard(port)

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 ZKLNECH+jcjosIwi002ovPDHLkV1naqes%x= literal 0 HcmV?d00001 diff --git "a/images/\347\203\210\351\205\222\347\272\247\351\206\222\351\205\222\345\212\251\346\211\213.png" "b/images/\347\203\210\351\205\222\347\272\247\351\206\222\351\205\222\345\212\251\346\211\213.png" new file mode 100644 index 0000000000000000000000000000000000000000..cd74254eb956289c0d9d31c8d41e52417b490684 GIT binary patch literal 87838 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