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"{col} | "
-
- # 如果是胜率表且有关系数据,添加额外的列
- 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"""
- {row_number}.
-
- {monster1_name}
-
- {monster2_name}
- | """
- 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"""
- {row_number}.
-
- {display_name}
- | """
- 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"""
- {row_number}.
-
- {monster_name}
- | """
-
- # 数据列
- for col in columns:
- value = row[col]
- if isinstance(value, float):
- if '率' in col or '程度' in col:
- html += f"{value:.2%} | "
- else:
- html += f"{value:.2f} | "
- else:
- html += f"{value} | "
-
- # 如果是胜率表且有关系数据,添加关系信息
- if not is_combo and monster_relations and title == '所有怪物胜率排行榜' and monster_id:
- relations = monster_relations.get(int(monster_id), {})
-
- # 最佳队友
- html += ""
- 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 += " | "
-
- # 克制关系
- 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 += " | "
-
- # 被克制关系
- 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 += " | "
-
- html += "
\n"
- row_number += 1
-
- 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"""
-
- {row_number}.
-
- {monster_name}
- |
- {effect['克制程度']:.2%} |
- {effect['装置胜率']:.2%} |
- {effect['普通胜率']:.2%} |
- {effect['装置场次']} |
- {effect['普通场次']} |
- {effect['效果类型']} |
-
\n"""
- row_number += 1
-
- html += "
\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..d82b354 100644
--- a/main.spec
+++ b/main.spec
@@ -11,6 +11,14 @@ a_main = Analysis(
pathex=[project_root],
binaries=[],
datas=[
+ ('vendor', 'vendor'),
+ ('src/resources', 'src/resources'),
+ ('src/simulation/arknights.csv', 'src/simulation'),
+ ('src/simulation/monsters.json', 'src/simulation'),
+ ('src/simulation/scene.json', 'src/simulation'),
+ ('src/tools/battlefield_composite/monster_images', 'src/tools/battlefield_composite/monster_images'),
+ ('src/tools/battlefield_recognize/class_to_idx.json', 'src/tools/battlefield_recognize'),
+ ('src/tools/battlefield_recognize/场地识别字段说明.txt', 'src/tools/battlefield_recognize'),
('.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 +27,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 +48,7 @@ exe_main = EXE(
a_main.scripts,
[],
exclude_binaries=True,
- name='main',
+ name='CannotMax',
debug=False,
bootloader_ignore_signals=False,
strip=False,
@@ -51,7 +59,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 +68,14 @@ a_multi = Analysis(
pathex=[project_root],
binaries=[],
datas=[
+ ('vendor', 'vendor'),
+ ('src/resources', 'src/resources'),
+ ('src/simulation/arknights.csv', 'src/simulation'),
+ ('src/simulation/monsters.json', 'src/simulation'),
+ ('src/simulation/scene.json', 'src/simulation'),
+ ('src/tools/battlefield_composite/monster_images', 'src/tools/battlefield_composite/monster_images'),
+ ('src/tools/battlefield_recognize/class_to_idx.json', 'src/tools/battlefield_recognize'),
+ ('src/tools/battlefield_recognize/场地识别字段说明.txt', 'src/tools/battlefield_recognize'),
('.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 +84,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 +112,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..7738074 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,8 @@ 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"]
+build = ["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..190ae0f
--- /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, TOOLS_DIR
+
+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_DIR / "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"{col} | "
+
+ # 如果是胜率表且有关系数据,添加额外的列
+ 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"""
+ {row_number}.
+ {monster_img_tag(monster1_orig, 30)}
+ {monster1_name}
+ {monster_img_tag(monster2_orig, 30)}
+ {monster2_name}
+ | """
+ 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"""
+ {row_number}.
+ {monster_img_tag(monster_orig, 50)}
+ {display_name}
+ | """
+ 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"""
+ {row_number}.
+ {monster_img_tag(monster_orig, 50)}
+ {monster_name}
+ | """
+
+ # 数据列
+ for col in columns:
+ value = row[col]
+ if isinstance(value, float):
+ if "率" in col or "程度" in col:
+ html += f"{value:.2%} | "
+ else:
+ html += f"{value:.2f} | "
+ else:
+ html += f"{value} | "
+
+ # 如果是胜率表且有关系数据,添加关系信息
+ if (
+ not is_combo
+ and monster_relations
+ and title == "所有怪物胜率排行榜"
+ and monster_id
+ ):
+ relations = monster_relations.get(int(monster_id), {})
+
+ # 最佳队友
+ html += ""
+ 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 += " | "
+
+ # 克制关系
+ 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 += " | "
+
+ # 被克制关系
+ 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 += " | "
+
+ html += "
\n"
+ row_number += 1
+
+ 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"""
+ |
+ {row_number}.
+ {monster_img_tag(monster_orig, 40)}
+ {monster_name}
+ |
+ {effect['克制程度']:.2%} |
+ {effect['装置胜率']:.2%} |
+ {effect['普通胜率']:.2%} |
+ {effect['装置场次']} |
+ {effect['普通场次']} |
+ {effect['效果类型']} |
+
\n"""
+ row_number += 1
+
+ html += "
\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..c11c525
--- /dev/null
+++ b/src/core/paths.py
@@ -0,0 +1,50 @@
+import sys
+from pathlib import Path
+
+
+if getattr(sys, 'frozen', False):
+ _INTERNAL_DIR = Path(sys._MEIPASS)
+ EXE_DIR = _INTERNAL_DIR.parent
+ PROJECT_ROOT = EXE_DIR
+ SRC_DIR = _INTERNAL_DIR / "src"
+else:
+ _INTERNAL_DIR = None
+ EXE_DIR = Path(__file__).resolve().parents[2]
+ PROJECT_ROOT = EXE_DIR
+ 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"
+VENDOR_DIR = PROJECT_ROOT / "vendor"
+MODELS_DIR = PROJECT_ROOT / "models"
+
+
+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 91%
rename from data_package.py
rename to src/data/data_package.py
index dbc90b5..1328656 100644
--- a/data_package.py
+++ b/src/data/data_package.py
@@ -3,11 +3,11 @@
import re
from datetime import datetime
import shutil
+from src.core.paths import PROJECT_ROOT
def create_zip_package(output_zip_path):
- # 定义文件和文件夹路径
- data_folder = Path("data")
+ data_folder = PROJECT_ROOT / "data"
# 获取所有符合日期格式的目录
date_pattern = re.compile(r"^\d{4}_\d{2}_\d{2}__\d{2}_\d{2}_\d{2}$")
@@ -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..b63fb56 100644
--- a/loadData.py
+++ b/src/data/load_data.py
@@ -5,21 +5,19 @@
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
+from src.core.paths import VENDOR_DIR
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 = str(VENDOR_DIR / "bin" / "platform-tools" / "adb.exe")
self.screen_width = 0
self.screen_height = 0
self.device_serial = adb_serial if adb_serial else ""
@@ -29,7 +27,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 +63,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 +88,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 +103,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 +130,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 +150,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 +206,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 +230,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 +284,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 +336,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 +353,7 @@ def capture_screenshot(self):
def click(self, point):
if not self.hwnd:
return
-
+
try:
# PC端点击需要重新获取一次ClientRect以防窗口大小改变
rect = win32gui.GetClientRect(self.hwnd)
@@ -327,17 +363,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 +391,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 +434,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 +464,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..a82b127 100644
--- a/maa_adb_connector.py
+++ b/src/game/maa_adb_connector.py
@@ -7,6 +7,7 @@
from pathlib import Path
import numpy as np
+from src.core.paths import VENDOR_DIR
logger = logging.getLogger(__name__)
@@ -15,9 +16,9 @@ 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",
+ VENDOR_DIR / "bin" / "maafw",
+ Path(sys.executable).parent / "vendor" / "bin" / "maafw",
+ Path.cwd() / "vendor" / "bin" / "maafw",
]
for p in candidates:
if p.is_dir() and any(p.glob("MaaFramework.dll")):
@@ -55,7 +56,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 +140,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 +187,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 +256,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 +271,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 +298,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 +344,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 +359,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 +379,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 +415,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 +521,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 +555,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 60%
rename from predict.py
rename to src/models/predict.py
index 59edb44..21e9a71 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,38 @@
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
+from src.core.paths import MODELS_DIR
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 +49,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,10 +61,13 @@ def get_device(prefer_gpu=True):
logger.info("Use torch with cpu")
return torch.device("cpu")
+
class CannotModel:
- def __init__(self, model_path="models"):
+ def __init__(self, model_path=None):
self.device = get_device()
self.is_model_loaded = False
+ if model_path is None:
+ model_path = str(MODELS_DIR)
self.model_path = self._resolve_model_path(model_path)
try:
self.load_model() # 初始化时加载模型
@@ -49,7 +84,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 +103,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 +146,9 @@ def load_model(self):
rf"未找到训练好的模型文件 {self.model_path},请先训练模型"
)
+ # 兼容项目重构前保存的整模型 pickle 路径
+ _register_legacy_model_aliases()
+
try:
model = torch.load(
self.model_path,
@@ -106,9 +156,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 +165,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 +187,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 +205,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 +262,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 +297,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 73%
rename from predict_onnx.py
rename to src/models/predict_onnx.py
index 31a527f..9262197 100644
--- a/predict_onnx.py
+++ b/src/models/predict_onnx.py
@@ -5,13 +5,17 @@
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
+from src.core.paths import MODELS_DIR
logger = logging.getLogger(__name__)
+
class CannotModel:
- def __init__(self, model_path="models"):
+ def __init__(self, model_path=None):
+ if model_path is None:
+ model_path = str(MODELS_DIR)
self.model_path = self._resolve_model_path(model_path)
self.is_model_loaded = False
try:
@@ -35,7 +39,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 +54,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 +100,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 +161,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 +188,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 +207,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..d1e1530 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, MODELS_DIR
+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_DIR
+
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": MODELS_DIR, # 存到哪里
"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 70%
rename from val.py
rename to src/models/val.py
index 2d83354..d8c9d45 100644
--- a/val.py
+++ b/src/models/val.py
@@ -1,7 +1,9 @@
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
+from src.core.paths import MODELS_DIR
+
def evaluate(model, data_loader, criterion, device):
model.eval()
@@ -16,8 +18,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 +64,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 +86,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(
+ str(MODELS_DIR / "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(str(MODELS_DIR / "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 67%
rename from field_recognition.py
rename to src/recognition/field_recognition.py
index 59344bb..73743ba 100644
--- a/field_recognition.py
+++ b/src/recognition/field_recognition.py
@@ -4,9 +4,9 @@
import logging
from pathlib import Path
from collections import defaultdict
+from src.core.paths import TOOLS_DIR, ensure_tmp_images_dir
from PIL import Image
import cv2
-import numpy as np
import torch
import torch.nn as nn
from torchvision import models, transforms
@@ -18,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},
@@ -43,23 +43,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 +64,7 @@ def __init__(self):
self.grouped_elements = {}
self.image_feature_columns = []
self.is_initialized = False
-
+
# 初始化场地识别模型
self._init_field_recognition()
@@ -77,19 +72,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")
+ model_dir = TOOLS_DIR / "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 +96,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 +146,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 +162,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 +191,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"
+ temp_image_path = str(ensure_tmp_images_dir() / "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 +229,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 57%
rename from simulator/simulate.py
rename to src/simulation/simulate.py
index 8b3a58b..aa6db5f 100644
--- a/simulator/simulate.py
+++ b/src/simulation/simulate.py
@@ -1,15 +1,11 @@
import json
-import math
-import random
-import time
-from enum import Enum
-import numpy as np
import pandas as pd
from tqdm import tqdm
from .battle_field import Battlefield, Faction
from .utils import MONSTER_MAPPING, VISUALIZATION_MODE
+from src.core.paths import SIMULATION_DIR
def process_battle_data(csv_path):
@@ -19,66 +15,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(SIMULATION_DIR / "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")
+ battle_data = process_battle_data(SIMULATION_DIR / "arknights.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 +98,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..8f62f0e 100644
--- a/simulator/utils.py
+++ b/src/simulation/utils.py
@@ -1,17 +1,16 @@
-
-
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
from typing import TYPE_CHECKING
from .vector2d import FastVector
+from src.core.paths import DATA_DIR
logger = logging.getLogger(__name__)
@@ -21,14 +20,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 +39,7 @@ class DamageType(Enum):
def __str__(self):
return self.value # 直接返回值字符串
+
class BuffType(Enum):
CHILL = 0
FROZEN = 1
@@ -45,15 +48,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 +72,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 +84,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 +123,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 +142,35 @@ 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=DATA_DIR / "monster_greenvine.csv",
+):
"""从CSV文件加载怪物ID和原始名称的映射"""
mapping = {}
try:
- with open(file_path, mode='r', encoding='utf-8-sig') as csvfile:
+ with file_path.open(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(
+ DATA_DIR / "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 62%
rename from tools/convert_model.py
rename to src/tools/convert_model.py
index f55852b..0a430d0 100644
--- a/tools/convert_model.py
+++ b/src/tools/convert_model.py
@@ -1,20 +1,23 @@
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
+from src.core.paths import MODELS_DIR
+
+model_path = str(MODELS_DIR / "best_model_full.pth")
-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 +26,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..13720b6 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, MODELS_DIR
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 = MODELS_DIR / "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..8e4b91d 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, MODELS_DIR
+
# ==============================================================================
# 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 = MODELS_DIR / "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..1ba74ad 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, PROJECT_ROOT, MODELS_DIR
# 配置参数(需要与训练时一致)
CONFIG = {
- "csv_path": "arknights.csv",
- "model_path": "models/best_model_full.pth",
+ "csv_path": str(PROJECT_ROOT / "data" / "raw" / "arknights.csv"),
+ "model_path": str(MODELS_DIR / "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..5d8d01e 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,9 @@ 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",
],
}
@@ -95,7 +89,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..e553e65 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,54 @@ 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]
+build = [
+ { name = "pyinstaller" },
+]
dev = [
{ name = "onnx" },
{ name = "onnxscript" },
- { name = "pyinstaller" },
+ { 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 = "types-toml" },
]
[package.metadata]
@@ -170,17 +185,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]
+build = [{ name = "pyinstaller", specifier = ">=6.15.0" }]
dev = [
{ name = "onnx", specifier = ">=1.19.0" },
{ name = "onnxscript", specifier = ">=0.7.0" },
- { name = "pyinstaller", specifier = ">=6.15.0" },
+ { name = "pandas-stubs" },
+ { name = "types-toml" },
]
[[package]]
@@ -311,7 +330,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 +342,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 +419,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 +501,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 +582,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 +621,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 +673,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 +724,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 +1039,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 +1057,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 +1154,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 +1229,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 +1284,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 +1303,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 +1394,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 +1466,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 +1622,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 +1642,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 +1662,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 +1682,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 +1715,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 +1754,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 +1772,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 +1792,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 +1822,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 +1858,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 +1877,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 +1915,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 +1963,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 +2005,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 +2021,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 +2069,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 +2151,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 +2189,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 +2206,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 +2234,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 +2305,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 +2387,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 +2442,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 +2730,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 +2958,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 +2994,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 +3048,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 +3130,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 +3180,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 +3246,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 +3328,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 +3408,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 +3525,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 +3561,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 +3580,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 +3661,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 +3701,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 +3812,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 +3831,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 +3901,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 +3930,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 +4025,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 +4075,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