From f6620d28947934f1f33edada64638f53e25b95a7 Mon Sep 17 00:00:00 2001 From: AbyssRei Date: Sun, 3 May 2026 04:46:36 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E8=A7=84=E8=8C=83=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E7=9B=AE=E5=BD=95=E7=BB=93=E6=9E=84=E4=B8=8E=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E5=88=86=E5=B1=82=EF=BC=8C=E6=8F=90=E5=8D=87=E5=8F=AF?= =?UTF-8?q?=E7=BB=B4=E6=8A=A4=E6=80=A7=E4=BB=A5=E7=AC=A6=E5=90=88=E6=A0=87?= =?UTF-8?q?=E5=87=86=E5=B7=A5=E7=A8=8B=E5=8C=96=E8=A6=81=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-and-release.yml | 4 +- .gitignore | 19 +- WinningRate_Statistics.py | 942 --------- ...7\211\210).bat" => iron_shark_launcher.bat | 152 +- main.py | 383 ++-- main.spec | 26 +- main_old.py | 650 ------ main_sim.py | 569 ++++-- models/update.md | 7 - multi_instance.py | 432 ++-- pyproject.toml | 5 +- simulator/.gitignore | 1 - simulator/scene.json | 58 - simulator/stats.py | 172 -- specialmonster.py | 37 - {models => src}/__init__.py | 0 {simulator => src/analysis}/__init__.py | 0 sim_mc.py => src/analysis/sim_mc.py | 330 ++- .../analysis/similar_history_match.py | 210 +- src/analysis/winning_rate_statistics.py | 1131 +++++++++++ .../core/__init__.py | 0 config.py => src/core/config.py | 31 +- constants.py => src/core/constants.py | 58 +- src/core/paths.py | 50 + src/data/__init__.py | 0 data_package.py => src/data/data_package.py | 8 +- loadData.py => src/data/load_data.py | 221 +- src/game/__init__.py | 0 auto_fetch.py => src/game/auto_fetch.py | 427 ++-- login.py => src/game/login.py | 318 ++- .../game/maa_adb_connector.py | 129 +- winrt_capture.py => src/game/winrt_capture.py | 60 +- src/models/__init__.py | 0 {models => src/models}/model.py | 14 +- {models => src/models}/muon.py | 55 +- predict.py => src/models/predict.py | 194 +- predict_onnx.py => src/models/predict_onnx.py | 96 +- train.py => src/models/train.py | 264 ++- val.py => src/models/val.py | 56 +- src/recognition/__init__.py | 0 .../recognition/field_recognition.py | 142 +- .../recognition/find_monster_zone.py | 367 +++- recognize.py => src/recognition/recognize.py | 146 +- src/recognition/specialmonster.py | 44 + .../resources/assets/icons}/background.png | Bin {ico => src/resources/assets/icons}/icon.ico | Bin .../resources/assets/icons}/icon_64x64.ico | Bin ...0\345\212\233\350\243\205\347\224\262.png" | Bin ...0\345\212\233\350\243\205\347\224\262.png" | Bin .../resources/assets/images}/eg.png | Bin .../resources/assets/images}/empty.png | Bin .../images}/login/announcement_close.png | Bin .../assets/images}/login/competition_page.png | Bin .../images}/login/event_claim_close.png | Bin .../assets/images}/login/login_button.png | Bin .../assets/images}/login/login_off.png | Bin .../resources/assets/images}/process/0.png | Bin .../resources/assets/images}/process/1.png | Bin .../resources/assets/images}/process/10.png | Bin .../resources/assets/images}/process/11.png | Bin .../resources/assets/images}/process/12.png | Bin .../resources/assets/images}/process/13.png | Bin .../resources/assets/images}/process/14.png | Bin .../resources/assets/images}/process/15.png | Bin .../images}/process/1746774840_28_5.png1s.png | Bin .../resources/assets/images}/process/2.png | Bin .../resources/assets/images}/process/3.png | Bin .../resources/assets/images}/process/4.png | Bin .../resources/assets/images}/process/5.png | Bin .../resources/assets/images}/process/6.png | Bin .../resources/assets/images}/process/7.png | Bin .../resources/assets/images}/process/8.png | Bin .../resources/assets/images}/process/9.png | Bin ...5\344\273\207\350\200\205\342\200\235.png" | Bin .../\342\200\234\345\246\222\342\200\235.png" | Bin ...4\345\267\242\347\251\264\342\200\235.png" | Bin ...4\345\272\236\350\264\235\342\200\235.png" | Bin ...5\347\237\263\346\234\272\342\200\235.png" | Bin ...4\347\233\233\346\200\222\342\200\235.png" | Bin ...4\350\207\252\345\234\250\342\200\235.png" | Bin ...4\346\204\217\345\277\227\342\200\235.png" | Bin ...3\347\224\237\351\243\216\342\200\235.png" | Bin .../\342\200\234\351\227\250\342\200\235.png" | Bin ...4\351\230\277\345\222\254\342\200\235.png" | Bin ...1\350\242\255\345\274\251\346\211\213.png" | Bin ...5\346\262\231\346\273\251\350\275\246.png" | Bin ...7\345\215\253\346\263\225\350\200\205.png" | Bin ...0\345\216\237\346\234\257\345\270\210.png" | Bin ...6\346\272\220\347\237\263\350\231\253.png" | Bin ...3\351\233\266\351\252\221\345\243\253.png" | Bin .../\345\207\266\350\261\225\345\205\275.png" | Bin ...5\347\224\262\346\255\245\345\205\265.png" | Bin ...2\345\267\250\345\262\251\350\233\233.png" | Bin ...3\345\233\242\345\255\246\345\276\222.png" | Bin ...7\345\207\277\347\237\263\350\200\205.png" | Bin .../\345\244\215\344\273\207\350\200\205.png" | Bin ...0\345\237\265\346\235\203\345\214\226.png" | Bin ...3\346\265\201\346\265\252\350\200\205.png" | Bin ...7\350\200\205\344\277\241\344\275\277.png" | Bin ...7\347\252\245\351\255\205\344\272\272.png" | Bin .../images/\345\262\201\347\233\270.png" | Bin .../\345\262\251\345\206\240\345\205\275.png" | Bin ...1\351\224\213\345\215\253\351\225\277.png" | Bin ...3\346\211\213\345\233\232\347\212\257.png" | Bin ...4\345\245\275\346\210\230\350\200\205.png" | Bin ...4\347\240\264\345\235\217\347\216\213.png" | Bin ...3\345\233\242\345\255\246\345\276\222.png" | Bin .../images/\346\227\272\350\264\242.png" | Bin ...0\351\243\237\344\272\272\350\212\261.png" | Bin ...0\351\243\237\350\231\253\350\212\261.png" | Bin ...2\346\216\250\350\215\220\350\200\205.png" | Bin ...1\345\273\211\345\247\206\346\226\257.png" | Bin ...0\346\261\275\346\210\230\350\275\246.png" | Bin .../\346\236\243\345\244\247\345\210\200.png" | Bin ...7\346\234\275\344\271\213\347\247\215.png" | Bin .../images/\346\243\230\345\205\275.png" | Bin ...0\345\233\242\351\274\223\346\211\213.png" | Bin ...0\345\205\213\346\226\257\346\211\213.png" | Bin ...4\346\211\213\351\207\215\350\211\207.png" | Bin ...4\351\201\201\345\277\215\350\200\205.png" | Bin .../images/\346\262\211\346\262\231.png" | Bin ...3\345\233\242\347\262\276\351\224\220.png" | Bin ...7\350\267\265\350\241\214\350\200\205.png" | Bin ...5\345\262\251\345\267\250\345\203\217.png" | Bin ...7\350\243\202\347\244\201\350\200\205.png" | Bin ...6\350\243\205\351\251\256\345\205\275.png" | Bin ...3\351\230\237\347\233\276\345\215\253.png" | Bin ...4\345\277\227\346\204\277\350\200\205.png" | Bin ...3\347\225\270\345\217\230\344\275\223.png" | Bin ...5\346\272\220\347\237\263\350\231\253.png" | Bin .../\347\202\255\351\225\277\347\237\233.png" | Bin ...6\345\207\273\347\273\204\351\225\277.png" | Bin ...0\346\272\220\347\237\263\350\231\253.png" | Bin ...7\344\270\273\347\273\204\351\225\277.png" | Bin ...47\232\204\347\214\216\347\213\227pro.png" | Bin ...1\345\207\273\346\255\245\345\205\265.png" | Bin .../\347\216\211\345\217\214\345\211\221.png" | Bin .../images/\347\224\260\351\274\267.png" | Bin ...0\350\265\230\347\224\237\347\211\251.png" | Bin .../resources/assets/images/\347\230\264.png" | Bin ...7\350\204\211\345\256\210\345\215\253.png" | Bin ...1\345\244\264\346\260\264\346\211\213.png" | Bin ...4\347\224\262\347\210\252\345\205\275.png" | Bin .../\347\251\272\351\231\215\345\205\265.png" | Bin ...0\346\233\262\345\220\210\345\243\260.png" | Bin ...0\350\264\245\351\252\221\345\243\253.png" | Bin ...3\345\220\211\344\273\226\342\200\235.png" | Bin ...4\350\264\235\346\226\257\342\200\235.png" | Bin ...7\345\212\253\346\216\240\350\200\205.png" | Bin ...1\345\244\247\345\211\221\346\211\213.png" | Bin ...2\347\231\276\345\244\253\351\225\277.png" | Bin ...5\345\220\236\345\231\254\350\200\205.png" | Bin ...7\346\234\275\345\256\277\345\215\253.png" | Bin ...7\346\234\275\346\210\230\350\275\246.png" | Bin ...0\351\233\206\345\205\273\350\200\205.png" | Bin ...6\344\272\272\345\274\251\346\211\213.png" | Bin ...2\346\211\260\344\271\261\350\200\205.png" | Bin ...7\347\232\204\345\267\250\345\203\217.png" | Bin ...7\237\263\350\231\253\302\267\316\261.png" | Bin ...5\351\230\262\345\276\241\350\200\205.png" | Bin ...0\345\207\273\344\272\272\346\211\213.png" | Bin ...7\346\234\257\345\270\210\342\200\235.png" | Bin ...0\345\241\224\346\234\257\345\270\210.png" | Bin ...0\346\231\256\345\260\274\345\205\213.png" | Bin ...6\350\243\205\344\272\272\345\221\230.png" | Bin ...5\346\272\220\347\237\263\350\231\253.png" | Bin ...4\346\272\220\347\237\263\350\231\253.png" | Bin src/resources/config/maa_option.json | 7 + monster.csv => src/resources/data/monster.csv | 0 .../resources/data/monster_greenvine.csv | 0 {simulator => src/simulation}/README.md | 0 src/simulation/__init__.py | 0 {simulator => src/simulation}/arknights.csv | 0 {simulator => src/simulation}/battle_field.py | 154 +- {simulator => src/simulation}/elemental.py | 50 +- {simulator => src/simulation}/monsters.json | 0 {simulator => src/simulation}/monsters.py | 623 ++++-- {simulator => src/simulation}/projectiles.py | 138 +- src/simulation/scene.json | 206 ++ {simulator => src/simulation}/simulate.py | 77 +- src/simulation/stats.py | 191 ++ {simulator => src/simulation}/utils.py | 60 +- {simulator => src/simulation}/vector2d.py | 57 +- {simulator => src/simulation}/zone.py | 57 +- {tools => src/tools}/HumanDataCheck.py | 240 ++- .../tools}/battlefield_composite/.gitignore | 0 .../battlefield_composite.py | 41 +- .../extract_webm_frames.py | 93 + .../monster_images/IM-1.png | Bin .../monster_images/battlefield_empty.jpg | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...4-\346\210\230\346\226\227-A_Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...4-\346\210\230\346\226\227-Move_A-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...4-\346\210\230\346\226\227-A_Move-x1.webm" | Bin ...\346\210\230\346\226\227-Move_red-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...-\346\210\230\346\226\227-C1_Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...4-\346\210\230\346\226\227-Attack-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...346\210\230\346\226\227-Move_Loop-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...346\210\230\346\226\227-Move_Loop-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...346\210\230\346\226\227-Move_Loop-x1.webm" | Bin ...346\210\230\346\226\227-Move_Loop-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin ...244-\346\210\230\346\226\227-Move-x1.webm" | Bin .../battlefield_recognize/class_to_idx.json | 0 ...7\346\256\265\350\257\264\346\230\216.txt" | 0 {tools => src/tools}/convert_model.py | 23 +- {tools => src/tools}/data_cleaning.py | 6 +- .../data_cleaning_with_field_recognize.py | 191 +- .../data_cleaning_with_field_recognize_gpu.py | 208 +- {tools => src/tools}/data_nanWriter.py | 7 +- src/tools/data_washer_new.py | 1383 +++++++++++++ .../tools/merge_data.py | 107 +- {tools => src/tools}/package.py | 16 +- src/ui/__init__.py | 0 .../ui/dark_mode_style_fix.py | 0 input_panel_ui.py => src/ui/input_panel_ui.py | 163 +- .../ui/similar_history_match_ui.py | 86 +- src/utils/__init__.py | 0 unit.py => src/utils/unit.py | 2 +- .../extract_webm_frames.py | 92 - tools/data_washer_new.py | 904 --------- uv.lock | 1807 +++++++++++------ {maafw => vendor/bin/maafw}/DirectML.dll | Bin .../bin/maafw}/MaaAdbControlUnit.dll | Bin .../bin/maafw}/MaaAgentClient.dll | Bin .../bin/maafw}/MaaAgentServer.dll | Bin .../bin/maafw}/MaaCustomControlUnit.dll | Bin {maafw => vendor/bin/maafw}/MaaFramework.dll | Bin .../bin/maafw}/MaaGamepadControlUnit.dll | Bin {maafw => vendor/bin/maafw}/MaaNode.node | Bin .../bin/maafw}/MaaNodeServer.node | Bin {maafw => vendor/bin/maafw}/MaaPiCli.exe | Bin .../bin/maafw}/MaaRecordControlUnit.dll | Bin .../bin/maafw}/MaaReplayControlUnit.dll | Bin {maafw => vendor/bin/maafw}/MaaToolkit.dll | Bin {maafw => vendor/bin/maafw}/MaaUtils.dll | Bin .../bin/maafw}/MaaWin32ControlUnit.dll | Bin {maafw => vendor/bin/maafw}/ViGEmClient.dll | Bin .../bin/maafw}/fastdeploy_ppocr_maa.dll | Bin .../bin/maafw}/onnxruntime_maa.dll | Bin .../bin/maafw}/opencv_world4_maa.dll | Bin .../bin/maafw}/plugins/MaaPluginDemo.dll | Bin .../bin/platform-tools}/AdbWinApi.dll | Bin .../bin/platform-tools}/AdbWinUsbApi.dll | Bin .../bin/platform-tools}/NOTICE.txt | 0 .../bin/platform-tools}/adb.exe | Bin .../bin/platform-tools}/etc1tool.exe | Bin .../bin/platform-tools}/fastboot.exe | Bin .../bin/platform-tools}/hprof-conv.exe | Bin .../bin/platform-tools}/libwinpthread-1.dll | Bin .../bin/platform-tools}/make_f2fs.exe | Bin .../platform-tools}/make_f2fs_casefold.exe | Bin .../bin/platform-tools}/mke2fs.conf | 0 .../bin/platform-tools}/mke2fs.exe | Bin .../bin/platform-tools}/source.properties | 0 .../bin/platform-tools}/sqlite3.exe | Bin 299 files changed, 9090 insertions(+), 5707 deletions(-) delete mode 100644 WinningRate_Statistics.py rename "\343\200\220\350\204\232\346\234\254\343\200\221\351\223\201\351\262\250\351\261\274\345\220\257\345\212\250\357\274\201(\345\270\246PyTorch\347\211\210\346\234\254\351\200\211\346\213\251\347\211\210).bat" => iron_shark_launcher.bat (62%) delete mode 100644 main_old.py delete mode 100644 models/update.md delete mode 100644 simulator/.gitignore delete mode 100644 simulator/scene.json delete mode 100644 simulator/stats.py delete mode 100644 specialmonster.py rename {models => src}/__init__.py (100%) rename {simulator => src/analysis}/__init__.py (100%) rename sim_mc.py => src/analysis/sim_mc.py (67%) rename similar_history_match.py => src/analysis/similar_history_match.py (74%) create mode 100644 src/analysis/winning_rate_statistics.py rename "data_train/package/\346\225\260\346\215\256\345\214\205\346\224\276\345\234\250\346\255\244\350\267\257\345\276\204.md" => src/core/__init__.py (100%) rename config.py => src/core/config.py (54%) rename constants.py => src/core/constants.py (89%) create mode 100644 src/core/paths.py create mode 100644 src/data/__init__.py rename data_package.py => src/data/data_package.py (91%) rename loadData.py => src/data/load_data.py (76%) create mode 100644 src/game/__init__.py rename auto_fetch.py => src/game/auto_fetch.py (74%) rename login.py => src/game/login.py (72%) rename maa_adb_connector.py => src/game/maa_adb_connector.py (82%) rename winrt_capture.py => src/game/winrt_capture.py (90%) create mode 100644 src/models/__init__.py rename {models => src/models}/model.py (94%) rename {models => src/models}/muon.py (75%) rename predict.py => src/models/predict.py (60%) rename predict_onnx.py => src/models/predict_onnx.py (73%) rename train.py => src/models/train.py (70%) rename val.py => src/models/val.py (70%) create mode 100644 src/recognition/__init__.py rename field_recognition.py => src/recognition/field_recognition.py (67%) rename find_monster_zone.py => src/recognition/find_monster_zone.py (58%) rename recognize.py => src/recognition/recognize.py (81%) create mode 100644 src/recognition/specialmonster.py rename {ico => src/resources/assets/icons}/background.png (100%) rename {ico => src/resources/assets/icons}/icon.ico (100%) rename {ico => src/resources/assets/icons}/icon_64x64.ico (100%) rename "images/R-11\347\252\201\345\207\273\345\212\250\345\212\233\350\243\205\347\224\262.png" => "src/resources/assets/images/R-11\347\252\201\345\207\273\345\212\250\345\212\233\350\243\205\347\224\262.png" (100%) rename "images/R-31\351\207\215\345\236\213\345\212\250\345\212\233\350\243\205\347\224\262.png" => "src/resources/assets/images/R-31\351\207\215\345\236\213\345\212\250\345\212\233\350\243\205\347\224\262.png" (100%) rename {images => src/resources/assets/images}/eg.png (100%) rename {images => src/resources/assets/images}/empty.png (100%) rename {images => src/resources/assets/images}/login/announcement_close.png (100%) rename {images => src/resources/assets/images}/login/competition_page.png (100%) rename {images => src/resources/assets/images}/login/event_claim_close.png (100%) rename {images => src/resources/assets/images}/login/login_button.png (100%) rename {images => src/resources/assets/images}/login/login_off.png (100%) rename {images => src/resources/assets/images}/process/0.png (100%) rename {images => src/resources/assets/images}/process/1.png (100%) rename {images => src/resources/assets/images}/process/10.png (100%) rename {images => src/resources/assets/images}/process/11.png (100%) rename {images => src/resources/assets/images}/process/12.png (100%) rename {images => src/resources/assets/images}/process/13.png (100%) rename {images => src/resources/assets/images}/process/14.png (100%) rename {images => src/resources/assets/images}/process/15.png (100%) rename {images => src/resources/assets/images}/process/1746774840_28_5.png1s.png (100%) rename {images => src/resources/assets/images}/process/2.png (100%) rename {images => src/resources/assets/images}/process/3.png (100%) rename {images => src/resources/assets/images}/process/4.png (100%) rename {images => src/resources/assets/images}/process/5.png (100%) rename {images => src/resources/assets/images}/process/6.png (100%) rename {images => src/resources/assets/images}/process/7.png (100%) rename {images => src/resources/assets/images}/process/8.png (100%) rename {images => src/resources/assets/images}/process/9.png (100%) rename "images/\342\200\234\345\244\215\344\273\207\350\200\205\342\200\235.png" => "src/resources/assets/images/\342\200\234\345\244\215\344\273\207\350\200\205\342\200\235.png" (100%) rename "images/\342\200\234\345\246\222\342\200\235.png" => "src/resources/assets/images/\342\200\234\345\246\222\342\200\235.png" (100%) rename "images/\342\200\234\345\267\242\347\251\264\342\200\235.png" => "src/resources/assets/images/\342\200\234\345\267\242\347\251\264\342\200\235.png" (100%) rename "images/\342\200\234\345\272\236\350\264\235\342\200\235.png" => "src/resources/assets/images/\342\200\234\345\272\236\350\264\235\342\200\235.png" (100%) rename "images/\342\200\234\346\212\225\347\237\263\346\234\272\342\200\235.png" => "src/resources/assets/images/\342\200\234\346\212\225\347\237\263\346\234\272\342\200\235.png" (100%) rename "images/\342\200\234\347\233\233\346\200\222\342\200\235.png" => "src/resources/assets/images/\342\200\234\347\233\233\346\200\222\342\200\235.png" (100%) rename "images/\342\200\234\350\207\252\345\234\250\342\200\235.png" => "src/resources/assets/images/\342\200\234\350\207\252\345\234\250\342\200\235.png" (100%) rename "images/\342\200\234\350\220\250\347\261\263\347\232\204\346\204\217\345\277\227\342\200\235.png" => "src/resources/assets/images/\342\200\234\350\220\250\347\261\263\347\232\204\346\204\217\345\277\227\342\200\235.png" (100%) rename "images/\342\200\234\351\222\263\351\222\263\347\224\237\351\243\216\342\200\235.png" => "src/resources/assets/images/\342\200\234\351\222\263\351\222\263\347\224\237\351\243\216\342\200\235.png" (100%) rename "images/\342\200\234\351\227\250\342\200\235.png" => "src/resources/assets/images/\342\200\234\351\227\250\342\200\235.png" (100%) rename "images/\342\200\234\351\230\277\345\222\254\342\200\235.png" => "src/resources/assets/images/\342\200\234\351\230\277\345\222\254\342\200\235.png" (100%) rename "images/\344\271\214\350\220\250\346\226\257\347\252\201\350\242\255\345\274\251\346\211\213.png" => "src/resources/assets/images/\344\271\214\350\220\250\346\226\257\347\252\201\350\242\255\345\274\251\346\211\213.png" (100%) rename "images/\345\205\250\345\260\201\351\227\255\346\262\231\346\273\251\350\275\246.png" => "src/resources/assets/images/\345\205\250\345\260\201\351\227\255\346\262\231\346\273\251\350\275\246.png" (100%) rename "images/\345\206\260\345\216\237\345\215\253\346\263\225\350\200\205.png" => "src/resources/assets/images/\345\206\260\345\216\237\345\215\253\346\263\225\350\200\205.png" (100%) rename "images/\345\206\260\345\216\237\346\234\257\345\270\210.png" => "src/resources/assets/images/\345\206\260\345\216\237\346\234\257\345\270\210.png" (100%) rename "images/\345\206\260\347\210\206\346\272\220\347\237\263\350\231\253.png" => "src/resources/assets/images/\345\206\260\347\210\206\346\272\220\347\237\263\350\231\253.png" (100%) rename "images/\345\207\213\351\233\266\351\252\221\345\243\253.png" => "src/resources/assets/images/\345\207\213\351\233\266\351\252\221\345\243\253.png" (100%) rename "images/\345\207\266\350\261\225\345\205\275.png" => "src/resources/assets/images/\345\207\266\350\261\225\345\205\275.png" (100%) rename "images/\345\217\215\350\243\205\347\224\262\346\255\245\345\205\265.png" => "src/resources/assets/images/\345\217\215\350\243\205\347\224\262\346\255\245\345\205\265.png" (100%) rename "images/\345\217\230\345\274\202\345\267\250\345\262\251\350\233\233.png" => "src/resources/assets/images/\345\217\230\345\274\202\345\267\250\345\262\251\350\233\233.png" (100%) rename "images/\345\221\274\345\225\270\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222.png" => "src/resources/assets/images/\345\221\274\345\225\270\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222.png" (100%) rename "images/\345\233\272\346\265\267\345\207\277\347\237\263\350\200\205.png" => "src/resources/assets/images/\345\233\272\346\265\267\345\207\277\347\237\263\350\200\205.png" (100%) rename "images/\345\244\215\344\273\207\350\200\205.png" => "src/resources/assets/images/\345\244\215\344\273\207\350\200\205.png" (100%) rename "images/\345\245\216\351\232\206\357\274\214\346\221\251\350\257\203\350\220\250\345\237\265\346\235\203\345\214\226.png" => "src/resources/assets/images/\345\245\216\351\232\206\357\274\214\346\221\251\350\257\203\350\220\250\345\237\265\346\235\203\345\214\226.png" (100%) rename "images/\345\256\277\344\270\273\346\265\201\346\265\252\350\200\205.png" => "src/resources/assets/images/\345\256\277\344\270\273\346\265\201\346\265\252\350\200\205.png" (100%) rename "images/\345\257\273\350\267\257\350\200\205\344\277\241\344\275\277.png" => "src/resources/assets/images/\345\257\273\350\267\257\350\200\205\344\277\241\344\275\277.png" (100%) rename "images/\345\261\261\346\265\267\344\274\227\347\252\245\351\255\205\344\272\272.png" => "src/resources/assets/images/\345\261\261\346\265\267\344\274\227\347\252\245\351\255\205\344\272\272.png" (100%) rename "images/\345\262\201\347\233\270.png" => "src/resources/assets/images/\345\262\201\347\233\270.png" (100%) rename "images/\345\262\251\345\206\240\345\205\275.png" => "src/resources/assets/images/\345\262\251\345\206\240\345\205\275.png" (100%) rename "images/\345\274\247\345\205\211\351\224\213\345\215\253\351\225\277.png" => "src/resources/assets/images/\345\274\247\345\205\211\351\224\213\345\215\253\351\225\277.png" (100%) rename "images/\346\213\263\346\211\213\345\233\232\347\212\257.png" => "src/resources/assets/images/\346\213\263\346\211\213\345\233\232\347\212\257.png" (100%) rename "images/\346\217\220\344\272\232\345\215\241\344\271\214\345\245\275\346\210\230\350\200\205.png" => "src/resources/assets/images/\346\217\220\344\272\232\345\215\241\344\271\214\345\245\275\346\210\230\350\200\205.png" (100%) rename "images/\346\217\220\344\272\232\345\215\241\344\271\214\347\240\264\345\235\217\347\216\213.png" => "src/resources/assets/images/\346\217\220\344\272\232\345\215\241\344\271\214\347\240\264\345\235\217\347\216\213.png" (100%) rename "images/\346\225\243\345\215\216\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222.png" => "src/resources/assets/images/\346\225\243\345\215\216\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222.png" (100%) rename "images/\346\227\272\350\264\242.png" => "src/resources/assets/images/\346\227\272\350\264\242.png" (100%) rename "images/\346\232\264\350\265\260\351\243\237\344\272\272\350\212\261.png" => "src/resources/assets/images/\346\232\264\350\265\260\351\243\237\344\272\272\350\212\261.png" (100%) rename "images/\346\232\264\350\265\260\351\243\237\350\231\253\350\212\261.png" => "src/resources/assets/images/\346\232\264\350\265\260\351\243\237\350\231\253\350\212\261.png" (100%) rename "images/\346\234\227\345\247\206\351\205\222\346\216\250\350\215\220\350\200\205.png" => "src/resources/assets/images/\346\234\227\345\247\206\351\205\222\346\216\250\350\215\220\350\200\205.png" (100%) rename "images/\346\235\260\346\226\257\351\241\277\302\267\345\250\201\345\273\211\345\247\206\346\226\257.png" => "src/resources/assets/images/\346\235\260\346\226\257\351\241\277\302\267\345\250\201\345\273\211\345\247\206\346\226\257.png" (100%) rename "images/\346\236\201\346\270\251\350\222\270\346\261\275\346\210\230\350\275\246.png" => "src/resources/assets/images/\346\236\201\346\270\251\350\222\270\346\261\275\346\210\230\350\275\246.png" (100%) rename "images/\346\236\243\345\244\247\345\210\200.png" => "src/resources/assets/images/\346\236\243\345\244\247\345\210\200.png" (100%) rename "images/\346\236\257\346\234\275\344\271\213\347\247\215.png" => "src/resources/assets/images/\346\236\257\346\234\275\344\271\213\347\247\215.png" (100%) rename "images/\346\243\230\345\205\275.png" => "src/resources/assets/images/\346\243\230\345\205\275.png" (100%) rename "images/\346\256\213\345\205\232\344\271\220\345\233\242\351\274\223\346\211\213.png" => "src/resources/assets/images/\346\256\213\345\205\232\344\271\220\345\233\242\351\274\223\346\211\213.png" (100%) rename "images/\346\256\213\345\205\232\350\220\250\345\205\213\346\226\257\346\211\213.png" => "src/resources/assets/images/\346\256\213\345\205\232\350\220\250\345\205\213\346\226\257\346\211\213.png" (100%) rename "images/\346\260\264\346\211\213\351\207\215\350\211\207.png" => "src/resources/assets/images/\346\260\264\346\211\213\351\207\215\350\211\207.png" (100%) rename "images/\346\260\264\351\201\201\345\277\215\350\200\205.png" => "src/resources/assets/images/\346\260\264\351\201\201\345\277\215\350\200\205.png" (100%) rename "images/\346\262\211\346\262\231.png" => "src/resources/assets/images/\346\262\211\346\262\231.png" (100%) rename "images/\346\262\270\350\241\200\351\252\221\345\243\253\345\233\242\347\262\276\351\224\220.png" => "src/resources/assets/images/\346\262\270\350\241\200\351\252\221\345\243\253\345\233\242\347\262\276\351\224\220.png" (100%) rename "images/\346\263\245\345\262\251\345\260\217\351\230\237\350\267\265\350\241\214\350\200\205.png" => "src/resources/assets/images/\346\263\245\345\262\251\345\260\217\351\230\237\350\267\265\350\241\214\350\200\205.png" (100%) rename "images/\346\263\245\345\262\251\345\267\250\345\203\217.png" => "src/resources/assets/images/\346\263\245\345\262\251\345\267\250\345\203\217.png" (100%) rename "images/\346\267\261\346\272\237\350\243\202\347\244\201\350\200\205.png" => "src/resources/assets/images/\346\267\261\346\272\237\350\243\202\347\244\201\350\200\205.png" (100%) rename "images/\346\270\251\351\241\272\347\232\204\346\255\246\350\243\205\351\251\256\345\205\275.png" => "src/resources/assets/images/\346\270\251\351\241\272\347\232\204\346\255\246\350\243\205\351\251\256\345\205\275.png" (100%) rename "images/\346\270\270\345\207\273\351\230\237\347\233\276\345\215\253.png" => "src/resources/assets/images/\346\270\270\345\207\273\351\230\237\347\233\276\345\215\253.png" (100%) rename "images/\346\271\226\347\225\224\345\277\227\346\204\277\350\200\205.png" => "src/resources/assets/images/\346\271\226\347\225\224\345\277\227\346\204\277\350\200\205.png" (100%) rename "images/\346\272\220\347\237\263\347\225\270\345\217\230\344\275\223.png" => "src/resources/assets/images/\346\272\220\347\237\263\347\225\270\345\217\230\344\275\223.png" (100%) rename "images/\347\201\274\347\203\255\346\272\220\347\237\263\350\231\253.png" => "src/resources/assets/images/\347\201\274\347\203\255\346\272\220\347\237\263\350\231\253.png" (100%) rename "images/\347\202\255\351\225\277\347\237\233.png" => "src/resources/assets/images/\347\202\255\351\225\277\347\237\233.png" (100%) rename "images/\347\202\256\345\207\273\347\273\204\351\225\277.png" => "src/resources/assets/images/\347\202\256\345\207\273\347\273\204\351\225\277.png" (100%) rename "images/\347\202\275\347\204\260\346\272\220\347\237\263\350\231\253.png" => "src/resources/assets/images/\347\202\275\347\204\260\346\272\220\347\237\263\350\231\253.png" (100%) rename "images/\347\213\202\346\232\264\345\256\277\344\270\273\347\273\204\351\225\277.png" => "src/resources/assets/images/\347\213\202\346\232\264\345\256\277\344\270\273\347\273\204\351\225\277.png" (100%) rename "images/\347\213\202\346\232\264\347\232\204\347\214\216\347\213\227pro.png" => "src/resources/assets/images/\347\213\202\346\232\264\347\232\204\347\214\216\347\213\227pro.png" (100%) rename "images/\347\213\231\345\207\273\346\255\245\345\205\265.png" => "src/resources/assets/images/\347\213\231\345\207\273\346\255\245\345\205\265.png" (100%) rename "images/\347\216\211\345\217\214\345\211\221.png" => "src/resources/assets/images/\347\216\211\345\217\214\345\211\221.png" (100%) rename "images/\347\224\260\351\274\267.png" => "src/resources/assets/images/\347\224\260\351\274\267.png" (100%) rename "images/\347\225\270\345\217\230\350\265\230\347\224\237\347\211\251.png" => "src/resources/assets/images/\347\225\270\345\217\230\350\265\230\347\224\237\347\211\251.png" (100%) rename "images/\347\230\264.png" => "src/resources/assets/images/\347\230\264.png" (100%) rename "images/\347\237\277\350\204\211\345\256\210\345\215\253.png" => "src/resources/assets/images/\347\237\277\350\204\211\345\256\210\345\215\253.png" (100%) rename "images/\347\240\201\345\244\264\346\260\264\346\211\213.png" => "src/resources/assets/images/\347\240\201\345\244\264\346\260\264\346\211\213.png" (100%) rename "images/\347\241\254\347\224\262\347\210\252\345\205\275.png" => "src/resources/assets/images/\347\241\254\347\224\262\347\210\252\345\205\275.png" (100%) rename "images/\347\251\272\351\231\215\345\205\265.png" => "src/resources/assets/images/\347\251\272\351\231\215\345\205\265.png" (100%) rename "images/\347\273\210\346\233\262\345\220\210\345\243\260.png" => "src/resources/assets/images/\347\273\210\346\233\262\345\220\210\345\243\260.png" (100%) rename "images/\350\205\220\350\264\245\351\252\221\345\243\253.png" => "src/resources/assets/images/\350\205\220\350\264\245\351\252\221\345\243\253.png" (100%) rename "images/\350\207\252\347\224\261\344\275\243\345\205\265\342\200\234\344\270\273\351\237\263\345\220\211\344\273\226\342\200\235.png" => "src/resources/assets/images/\350\207\252\347\224\261\344\275\243\345\205\265\342\200\234\344\270\273\351\237\263\345\220\211\344\273\226\342\200\235.png" (100%) rename "images/\350\207\252\347\224\261\344\275\243\345\205\265\342\200\234\350\264\235\346\226\257\342\200\235.png" => "src/resources/assets/images/\350\207\252\347\224\261\344\275\243\345\205\265\342\200\234\350\264\235\346\226\257\342\200\235.png" (100%) rename "images/\350\215\222\345\216\237\345\212\253\346\216\240\350\200\205.png" => "src/resources/assets/images/\350\215\222\345\216\237\345\212\253\346\216\240\350\200\205.png" (100%) rename "images/\350\220\250\345\215\241\345\205\271\345\244\247\345\211\221\346\211\213.png" => "src/resources/assets/images/\350\220\250\345\215\241\345\205\271\345\244\247\345\211\221\346\211\213.png" (100%) rename "images/\350\220\250\345\215\241\345\205\271\345\256\277\344\270\273\345\215\253\345\267\242\347\231\276\345\244\253\351\225\277.png" => "src/resources/assets/images/\350\220\250\345\215\241\345\205\271\345\256\277\344\270\273\345\215\253\345\267\242\347\231\276\345\244\253\351\225\277.png" (100%) rename "images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\345\220\236\345\231\254\350\200\205.png" => "src/resources/assets/images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\345\220\236\345\231\254\350\200\205.png" (100%) rename "images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\345\256\277\345\215\253.png" => "src/resources/assets/images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\345\256\277\345\215\253.png" (100%) rename "images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\346\210\230\350\275\246.png" => "src/resources/assets/images/\350\220\250\345\215\241\345\205\271\346\236\257\346\234\275\346\210\230\350\275\246.png" (100%) rename "images/\350\257\241\346\240\270\351\233\206\345\205\273\350\200\205.png" => "src/resources/assets/images/\350\257\241\346\240\270\351\233\206\345\205\273\350\200\205.png" (100%) rename "images/\350\265\217\351\207\221\347\214\216\344\272\272\345\274\251\346\211\213.png" => "src/resources/assets/images/\350\265\217\351\207\221\347\214\216\344\272\272\345\274\251\346\211\213.png" (100%) rename "images/\350\265\217\351\207\221\347\214\216\344\272\272\346\211\260\344\271\261\350\200\205.png" => "src/resources/assets/images/\350\265\217\351\207\221\347\214\216\344\272\272\346\211\260\344\271\261\350\200\205.png" (100%) rename "images/\350\277\267\350\267\257\347\232\204\345\267\250\345\203\217.png" => "src/resources/assets/images/\350\277\267\350\267\257\347\232\204\345\267\250\345\203\217.png" (100%) rename "images/\351\205\270\346\266\262\346\272\220\347\237\263\350\231\253\302\267\316\261.png" => "src/resources/assets/images/\351\205\270\346\266\262\346\272\220\347\237\263\350\231\253\302\267\316\261.png" (100%) rename "images/\351\207\215\350\243\205\351\230\262\345\276\241\350\200\205.png" => "src/resources/assets/images/\351\207\215\350\243\205\351\230\262\345\276\241\350\200\205.png" (100%) rename "images/\351\230\265\345\234\260\345\207\273\344\272\272\346\211\213.png" => "src/resources/assets/images/\351\230\265\345\234\260\345\207\273\344\272\272\346\211\213.png" (100%) rename "images/\351\243\216\346\203\205\350\241\227\342\200\234\346\230\237\346\234\257\345\270\210\342\200\235.png" => "src/resources/assets/images/\351\243\216\346\203\205\350\241\227\342\200\234\346\230\237\346\234\257\345\270\210\342\200\235.png" (100%) rename "images/\351\253\230\345\241\224\346\234\257\345\270\210.png" => "src/resources/assets/images/\351\253\230\345\241\224\346\234\257\345\270\210.png" (100%) rename "images/\351\253\230\346\231\256\345\260\274\345\205\213.png" => "src/resources/assets/images/\351\253\230\346\231\256\345\260\274\345\205\213.png" (100%) rename "images/\351\253\230\347\272\247\346\255\246\350\243\205\344\272\272\345\221\230.png" => "src/resources/assets/images/\351\253\230\347\272\247\346\255\246\350\243\205\344\272\272\345\221\230.png" (100%) rename "images/\351\253\230\350\203\275\346\272\220\347\237\263\350\231\253.png" => "src/resources/assets/images/\351\253\230\350\203\275\346\272\220\347\237\263\350\231\253.png" (100%) rename "images/\351\273\221\346\260\264\346\272\220\347\237\263\350\231\253.png" => "src/resources/assets/images/\351\273\221\346\260\264\346\272\220\347\237\263\350\231\253.png" (100%) create mode 100644 src/resources/config/maa_option.json rename monster.csv => src/resources/data/monster.csv (100%) rename monster_greenvine.csv => src/resources/data/monster_greenvine.csv (100%) rename {simulator => src/simulation}/README.md (100%) create mode 100644 src/simulation/__init__.py rename {simulator => src/simulation}/arknights.csv (100%) rename {simulator => src/simulation}/battle_field.py (60%) rename {simulator => src/simulation}/elemental.py (82%) rename {simulator => src/simulation}/monsters.json (100%) rename {simulator => src/simulation}/monsters.py (80%) rename {simulator => src/simulation}/projectiles.py (52%) create mode 100644 src/simulation/scene.json rename {simulator => src/simulation}/simulate.py (57%) create mode 100644 src/simulation/stats.py rename {simulator => src/simulation}/utils.py (80%) rename {simulator => src/simulation}/vector2d.py (85%) rename {simulator => src/simulation}/zone.py (70%) rename {tools => src/tools}/HumanDataCheck.py (58%) rename {tools => src/tools}/battlefield_composite/.gitignore (100%) rename {tools => src/tools}/battlefield_composite/battlefield_composite.py (87%) create mode 100644 src/tools/battlefield_composite/extract_webm_frames.py rename {tools => src/tools}/battlefield_composite/monster_images/IM-1.png (100%) rename {tools => src/tools}/battlefield_composite/monster_images/battlefield_empty.jpg (100%) rename "tools/battlefield_composite/monster_images/\342\200\234\345\244\215\344\273\207\350\200\205\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\342\200\234\345\244\215\344\273\207\350\200\205\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\342\200\234\345\272\236\350\264\235\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\342\200\234\345\272\236\350\264\235\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\342\200\234\346\212\225\347\237\263\346\234\272\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-A_Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\342\200\234\346\212\225\347\237\263\346\234\272\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-A_Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\342\200\234\350\207\252\345\234\250\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\342\200\234\350\207\252\345\234\250\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\342\200\234\351\222\263\351\222\263\347\224\237\351\243\216\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\342\200\234\351\222\263\351\222\263\347\224\237\351\243\216\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\342\200\234\351\227\250\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\342\200\234\351\227\250\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\342\200\234\351\230\277\345\222\254\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\342\200\234\351\230\277\345\222\254\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\345\206\260\345\216\237\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\345\206\260\345\216\237\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\345\206\260\347\210\206\346\272\220\347\237\263\350\231\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\345\206\260\347\210\206\346\272\220\347\237\263\350\231\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\345\217\215\350\243\205\347\224\262\346\255\245\345\205\265-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_A-x1.webm" => "src/tools/battlefield_composite/monster_images/\345\217\215\350\243\205\347\224\262\346\255\245\345\205\265-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_A-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\345\217\230\345\274\202\345\267\250\345\262\251\350\233\233-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\345\217\230\345\274\202\345\267\250\345\262\251\350\233\233-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\345\221\274\345\225\270\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\345\221\274\345\225\270\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\345\233\272\346\265\267\345\207\277\347\237\263\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\345\233\272\346\265\267\345\207\277\347\237\263\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\345\244\215\344\273\207\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\345\244\215\344\273\207\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\345\256\277\344\270\273\346\213\276\350\215\222\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\345\256\277\344\270\273\346\213\276\350\215\222\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\345\257\214\350\220\245\345\205\273\347\232\204\347\251\277\345\210\272\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\345\257\214\350\220\245\345\205\273\347\232\204\347\251\277\345\210\272\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\345\261\261\346\265\267\344\274\227\347\252\245\351\255\205\344\272\272-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\345\261\261\346\265\267\344\274\227\347\252\245\351\255\205\344\272\272-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\345\261\261\351\233\252\351\254\274-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\345\261\261\351\233\252\351\254\274-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\345\274\247\345\205\211\351\224\213\345\215\253\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\345\274\247\345\205\211\351\224\213\345\215\253\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\211\216\347\275\227\357\274\214\342\200\234\347\213\274\344\271\213\344\270\273\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-A_Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\211\216\347\275\227\357\274\214\342\200\234\347\213\274\344\271\213\344\270\273\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-A_Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\213\263\346\211\213\345\233\232\347\212\257-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_red-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\213\263\346\211\213\345\233\232\347\212\257-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_red-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\217\220\344\272\232\345\215\241\344\271\214\345\245\275\346\210\230\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\217\220\344\272\232\345\215\241\344\271\214\345\245\275\346\210\230\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\217\220\344\272\232\345\215\241\344\271\214\347\240\264\345\235\217\347\216\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\217\220\344\272\232\345\215\241\344\271\214\347\240\264\345\235\217\347\216\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\225\243\345\215\216\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\225\243\345\215\216\351\252\221\345\243\253\345\233\242\345\255\246\345\276\222-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\234\227\345\247\206\351\205\222\346\216\250\350\215\220\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\234\227\345\247\206\351\205\222\346\216\250\350\215\220\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\234\250\350\243\202\346\210\230\345\243\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\234\250\350\243\202\346\210\230\345\243\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\235\260\346\226\257\351\241\277\302\267\345\250\201\345\273\211\345\247\206\346\226\257-\351\273\230\350\256\244-\346\210\230\346\226\227-C1_Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\235\260\346\226\257\351\241\277\302\267\345\250\201\345\273\211\345\247\206\346\226\257-\351\273\230\350\256\244-\346\210\230\346\226\227-C1_Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\237\223\346\261\241\350\272\257\345\243\263-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\237\223\346\261\241\350\272\257\345\243\263-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\256\213\345\205\232\350\220\250\345\205\213\346\226\257\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\256\213\345\205\232\350\220\250\345\205\213\346\226\257\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\262\270\350\241\200\351\252\221\345\243\253\345\233\242\347\262\276\351\224\220-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\262\270\350\241\200\351\252\221\345\243\253\345\233\242\347\262\276\351\224\220-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\263\245\345\262\251\345\267\250\345\203\217-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\263\245\345\262\251\345\267\250\345\203\217-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\267\261\346\272\237\350\243\202\347\244\201\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\267\261\346\272\237\350\243\202\347\244\201\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\270\251\351\241\272\347\232\204\346\255\246\350\243\205\351\251\256\345\205\275-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\270\251\351\241\272\347\232\204\346\255\246\350\243\205\351\251\256\345\205\275-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\271\226\347\225\224\345\277\227\346\204\277\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\271\226\347\225\224\345\277\227\346\204\277\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\346\272\220\347\237\263\347\225\270\345\217\230\344\275\223-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\346\272\220\347\237\263\347\225\270\345\217\230\344\275\223-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\347\201\260\345\260\276\351\246\231\344\270\273-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\347\201\260\345\260\276\351\246\231\344\270\273-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\347\202\256\345\207\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Attack-x1.webm" => "src/tools/battlefield_composite/monster_images/\347\202\256\345\207\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Attack-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\347\202\256\345\207\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\347\202\256\345\207\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\347\203\210\351\205\222\347\272\247\351\206\222\351\205\222\345\212\251\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\347\203\210\351\205\222\347\272\247\351\206\222\351\205\222\345\212\251\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\347\213\202\346\232\264\345\256\277\344\270\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\347\213\202\346\232\264\345\256\277\344\270\273\347\273\204\351\225\277-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\347\213\202\346\232\264\347\232\204\347\214\216\347\213\227pro-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" => "src/tools/battlefield_composite/monster_images/\347\213\202\346\232\264\347\232\204\347\214\216\347\213\227pro-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\347\224\260\351\274\267\345\212\233\345\243\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\347\224\260\351\274\267\345\212\233\345\243\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\347\237\277\350\204\211\345\256\210\345\215\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\347\237\277\350\204\211\345\256\210\345\215\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\347\245\236\345\260\204\346\211\213\345\233\232\347\212\257-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\347\245\236\345\260\204\346\211\213\345\233\232\347\212\257-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\345\244\247\345\211\221\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" => "src/tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\345\244\247\345\211\221\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\345\255\220\350\243\224\351\223\276\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\345\255\220\350\243\224\351\223\276\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\347\216\213\345\272\255\345\206\233\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\350\220\250\345\215\241\345\205\271\347\216\213\345\272\255\345\206\233\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\351\200\220\350\205\220\345\205\275-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\351\200\220\350\205\220\345\205\275-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\351\205\270\346\266\262\346\272\220\347\237\263\350\231\253\302\267\316\261-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" => "src/tools/battlefield_composite/monster_images/\351\205\270\346\266\262\346\272\220\347\237\263\350\231\253\302\267\316\261-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\351\207\215\350\243\205\351\230\262\345\276\241\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" => "src/tools/battlefield_composite/monster_images/\351\207\215\350\243\205\351\230\262\345\276\241\350\200\205-\351\273\230\350\256\244-\346\210\230\346\226\227-Move_Loop-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\351\230\265\345\234\260\345\207\273\344\272\272\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\351\230\265\345\234\260\345\207\273\344\272\272\346\211\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\351\243\216\346\203\205\350\241\227\342\200\234\346\230\237\346\234\257\345\270\210\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\351\243\216\346\203\205\350\241\227\342\200\234\346\230\237\346\234\257\345\270\210\342\200\235-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\351\253\230\345\241\224\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\351\253\230\345\241\224\346\234\257\345\270\210-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\351\253\230\346\231\256\345\260\274\345\205\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\351\253\230\346\231\256\345\260\274\345\205\213-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\351\253\230\347\272\247\346\255\246\350\243\205\344\272\272\345\221\230-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\351\253\230\347\272\247\346\255\246\350\243\205\344\272\272\345\221\230-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename "tools/battlefield_composite/monster_images/\351\253\230\350\203\275\346\272\220\347\237\263\350\231\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" => "src/tools/battlefield_composite/monster_images/\351\253\230\350\203\275\346\272\220\347\237\263\350\231\253-\351\273\230\350\256\244-\346\210\230\346\226\227-Move-x1.webm" (100%) rename {tools => src/tools}/battlefield_recognize/class_to_idx.json (100%) rename "tools/battlefield_recognize/\345\234\272\345\234\260\350\257\206\345\210\253\345\255\227\346\256\265\350\257\264\346\230\216.txt" => "src/tools/battlefield_recognize/\345\234\272\345\234\260\350\257\206\345\210\253\345\255\227\346\256\265\350\257\264\346\230\216.txt" (100%) rename {tools => src/tools}/convert_model.py (62%) rename {tools => src/tools}/data_cleaning.py (97%) rename {tools => src/tools}/data_cleaning_with_field_recognize.py (61%) rename {tools => src/tools}/data_cleaning_with_field_recognize_gpu.py (61%) rename {tools => src/tools}/data_nanWriter.py (94%) create mode 100644 src/tools/data_washer_new.py rename "data_train/\346\225\260\346\215\256\345\220\210\345\271\266.py" => src/tools/merge_data.py (69%) rename {tools => src/tools}/package.py (93%) create mode 100644 src/ui/__init__.py rename dark_mode_style_fix.py => src/ui/dark_mode_style_fix.py (100%) rename input_panel_ui.py => src/ui/input_panel_ui.py (80%) rename simular_history_match_ui.py => src/ui/similar_history_match_ui.py (85%) create mode 100644 src/utils/__init__.py rename unit.py => src/utils/unit.py (94%) delete mode 100644 tools/battlefield_composite/extract_webm_frames.py delete mode 100644 tools/data_washer_new.py rename {maafw => vendor/bin/maafw}/DirectML.dll (100%) rename {maafw => vendor/bin/maafw}/MaaAdbControlUnit.dll (100%) rename {maafw => vendor/bin/maafw}/MaaAgentClient.dll (100%) rename {maafw => vendor/bin/maafw}/MaaAgentServer.dll (100%) rename {maafw => vendor/bin/maafw}/MaaCustomControlUnit.dll (100%) rename {maafw => vendor/bin/maafw}/MaaFramework.dll (100%) rename {maafw => vendor/bin/maafw}/MaaGamepadControlUnit.dll (100%) rename {maafw => vendor/bin/maafw}/MaaNode.node (100%) rename {maafw => vendor/bin/maafw}/MaaNodeServer.node (100%) rename {maafw => vendor/bin/maafw}/MaaPiCli.exe (100%) rename {maafw => vendor/bin/maafw}/MaaRecordControlUnit.dll (100%) rename {maafw => vendor/bin/maafw}/MaaReplayControlUnit.dll (100%) rename {maafw => vendor/bin/maafw}/MaaToolkit.dll (100%) rename {maafw => vendor/bin/maafw}/MaaUtils.dll (100%) rename {maafw => vendor/bin/maafw}/MaaWin32ControlUnit.dll (100%) rename {maafw => vendor/bin/maafw}/ViGEmClient.dll (100%) rename {maafw => vendor/bin/maafw}/fastdeploy_ppocr_maa.dll (100%) rename {maafw => vendor/bin/maafw}/onnxruntime_maa.dll (100%) rename {maafw => vendor/bin/maafw}/opencv_world4_maa.dll (100%) rename {maafw => vendor/bin/maafw}/plugins/MaaPluginDemo.dll (100%) rename {platform-tools => vendor/bin/platform-tools}/AdbWinApi.dll (100%) rename {platform-tools => vendor/bin/platform-tools}/AdbWinUsbApi.dll (100%) rename {platform-tools => vendor/bin/platform-tools}/NOTICE.txt (100%) rename {platform-tools => vendor/bin/platform-tools}/adb.exe (100%) rename {platform-tools => vendor/bin/platform-tools}/etc1tool.exe (100%) rename {platform-tools => vendor/bin/platform-tools}/fastboot.exe (100%) rename {platform-tools => vendor/bin/platform-tools}/hprof-conv.exe (100%) rename {platform-tools => vendor/bin/platform-tools}/libwinpthread-1.dll (100%) rename {platform-tools => vendor/bin/platform-tools}/make_f2fs.exe (100%) rename {platform-tools => vendor/bin/platform-tools}/make_f2fs_casefold.exe (100%) rename {platform-tools => vendor/bin/platform-tools}/mke2fs.conf (100%) rename {platform-tools => vendor/bin/platform-tools}/mke2fs.exe (100%) rename {platform-tools => vendor/bin/platform-tools}/source.properties (100%) rename {platform-tools => vendor/bin/platform-tools}/sqlite3.exe (100%) diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index 73a4c53..eadd33c 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -24,13 +24,13 @@ jobs: run: uv sync --group dev - name: Build with PyInstaller - run: uv run tools/package.py + run: uv run src/tools/package.py - name: Upload build artifact uses: actions/upload-artifact@v7 with: name: cannotmax-build - path: output/main/ + path: build/dist/main/ retention-days: 1 release: diff --git a/.gitignore b/.gitignore index 5781d3d..14e3469 100644 --- a/.gitignore +++ b/.gitignore @@ -30,27 +30,17 @@ __pycache__/ # 5. 模型权重与构建产物 # =========================== # 模型文件 -models/*.png -*.pth -*.onnx -*.onnx.data +/models/ # 构建与输出目录 -build/ -output/ +/build/ # =========================== # 6. 数据集与媒体资源 # =========================== # 基础数据目录 -data/images/ -data/[0-9][0-9][0-9][0-9]_[0-1][0-9]_[0-3][0-9]__[0-2][0-9]_[0-5][0-9]_[0-5][0-9]/ -images/nums/ - -# 训练相关数据 -data_train/tmp/ -data_train/package/*.zip -data_train/images/ +src/resources/assets/images/nums/ +/data/ # 单独的截图文件 screenshot.png @@ -68,5 +58,4 @@ tmp # =========================== # 8. 其他杂项文件 # =========================== -arknights.csv multi_ports.txt diff --git a/WinningRate_Statistics.py b/WinningRate_Statistics.py deleted file mode 100644 index c6d9aad..0000000 --- a/WinningRate_Statistics.py +++ /dev/null @@ -1,942 +0,0 @@ -import pandas as pd -import numpy as np -from pathlib import Path -from math import sqrt -import csv -from collections import defaultdict -from config import MONSTER_COUNT, FIELD_FEATURE_COUNT, MONSTER_DATA - -FIELD_FEATURE_COUNT=0 -def load_data(): - """加载数据""" - df = pd.read_csv('arknights.csv', header=None, low_memory=False) - # 设置列名 - monster_cols_left = [f'L{i+1}' for i in range(MONSTER_COUNT)] - field_cols_left = [f'FL{i+1}' for i in range(FIELD_FEATURE_COUNT)] - monster_cols_right = [f'R{i+1}' for i in range(MONSTER_COUNT)] - field_cols_right = [f'FR{i+1}' for i in range(FIELD_FEATURE_COUNT)] - - df.columns = monster_cols_left + field_cols_left + monster_cols_right + field_cols_right + ['Result', 'ImgPath'] - return df - - -def get_monster_name(monster_id): - """根据怪物ID获取怪物名称""" - if monster_id in MONSTER_DATA.index: - return MONSTER_DATA.loc[monster_id]['名称'] - return f'怪物{monster_id}' - - -def get_monster_original_name(monster_id): - """根据怪物ID获取怪物原始名称(用于匹配图片)""" - if monster_id in MONSTER_DATA.index: - return MONSTER_DATA.loc[monster_id]['原始名称'] - return f'怪物{monster_id}' - - -def calculate_all_monster_win_rates(df): - """计算所有怪物的胜率""" - monster_stats = {} - total_matches = len(df) - - for i in range(1, MONSTER_COUNT + 1): - try: - # 转换数据类型并过滤 - df[f'L{i}'] = pd.to_numeric(df[f'L{i}'], errors='coerce').fillna(0) - df[f'R{i}'] = pd.to_numeric(df[f'R{i}'], errors='coerce').fillna(0) - - # 左方统计 - left_games = df[df[f'L{i}'] != 0] - left_wins = len(left_games[left_games['Result'] == 'L']) - left_total = len(left_games) - - # 右方统计 - right_games = df[df[f'R{i}'] != 0] - right_wins = len(right_games[right_games['Result'] == 'R']) - right_total = len(right_games) - except Exception as e: - print(f"处理怪物{i}时出错: {e}") - continue - - # 合并统计 - total_games = left_total + right_total - total_wins = left_wins + right_wins - - if total_games > 0: - monster_name = get_monster_name(i) - win_rate = total_wins / total_games - participation_rate = total_games / total_matches if total_matches > 0 else 0 - monster_stats[monster_name] = { - '怪物ID': i, - '胜场': total_wins, - '总场数': total_games, - '胜率': win_rate, - '参战率': participation_rate - } - - return pd.DataFrame(monster_stats).T.sort_values('胜率', ascending=False) - - -def analyze_monster_combinations(df): - """分析怪物配合效果""" - # 初始化数据结构 - single_stats = defaultdict(lambda: {'appearances': 0, 'wins': 0}) - pair_stats = defaultdict(lambda: {'co_occurrences': 0, 'co_wins': 0}) - - for _, record in df.iterrows(): - victory_side = record['Result'] - - # 获取左右两方的怪物 - left_monsters = [] - right_monsters = [] - - for i in range(1, MONSTER_COUNT + 1): - try: - left_val = float(record[f'L{i}']) if pd.notna(record[f'L{i}']) else 0 - right_val = float(record[f'R{i}']) if pd.notna(record[f'R{i}']) else 0 - - if left_val > 0: - left_monsters.append(i) - if right_val > 0: - right_monsters.append(i) - except (ValueError, TypeError): - continue - - # 确定胜利队伍和失败队伍 - if victory_side == 'L': - win_team, lose_team = left_monsters, right_monsters - else: - win_team, lose_team = right_monsters, left_monsters - - # 更新单怪统计(胜利方) - for monster in win_team: - single_stats[monster]['appearances'] += 1 - single_stats[monster]['wins'] += 1 - - # 更新单怪统计(失败方) - for monster in lose_team: - single_stats[monster]['appearances'] += 1 - - # 更新双怪组合统计(胜利方) - for i in range(len(win_team)): - for j in range(i+1, len(win_team)): - x, y = sorted((win_team[i], win_team[j])) - pair_stats[(x, y)]['co_occurrences'] += 1 - pair_stats[(x, y)]['co_wins'] += 1 - - # 更新双怪组合统计(失败方) - for i in range(len(lose_team)): - for j in range(i+1, len(lose_team)): - x, y = sorted((lose_team[i], lose_team[j])) - pair_stats[(x, y)]['co_occurrences'] += 1 - - # 计算最佳配合 - results = [] - total_battles = len(df) - - for (x, y), stats in pair_stats.items(): - if stats['co_occurrences'] < 10: # 过滤低频组合 - continue - - # 组合胜率 - xy_win_rate = stats['co_wins'] / stats['co_occurrences'] - - # 单怪胜率 - if single_stats[x]['appearances'] > 0 and single_stats[y]['appearances'] > 0: - x_win_rate = single_stats[x]['wins'] / single_stats[x]['appearances'] - y_win_rate = single_stats[y]['wins'] / single_stats[y]['appearances'] - - # 提升度 - 简化计算,不使用卡方检验 - expected_win_rate = sqrt(x_win_rate * y_win_rate) - lift = xy_win_rate / expected_win_rate if expected_win_rate > 0 else 0 - - if lift > 1.1 and xy_win_rate > max(x_win_rate, y_win_rate) and stats['co_occurrences'] >= 20: - x_name = get_monster_name(x) - y_name = get_monster_name(y) - results.append({ - '组合': f'{x_name}+{y_name}', - '怪物1': x_name, - '怪物2': y_name, - 'ID1': x, - 'ID2': y, - '提升度': lift, - '组合胜率': xy_win_rate, - '出场次数': stats['co_occurrences'], - '获胜次数': stats['co_wins'] - }) - - # 按提升度排序 - results.sort(key=lambda x: -x['提升度']) - return pd.DataFrame(results) - - -def find_countered_monsters(df): - """寻找被克制的怪物前五个""" - counter_stats = defaultdict(lambda: {'total_matchups': 0, 'losses': 0}) - - for _, record in df.iterrows(): - victory_side = record['Result'] - - # 获取左右两方的怪物 - left_monsters = [] - right_monsters = [] - - for i in range(1, MONSTER_COUNT + 1): - try: - left_val = float(record[f'L{i}']) if pd.notna(record[f'L{i}']) else 0 - right_val = float(record[f'R{i}']) if pd.notna(record[f'R{i}']) else 0 - - if left_val > 0: - left_monsters.append(i) - if right_val > 0: - right_monsters.append(i) - except (ValueError, TypeError): - continue - - # 分析对战情况 - for left_monster in left_monsters: - for right_monster in right_monsters: - # 左方怪物的统计 - counter_stats[left_monster]['total_matchups'] += 1 - if victory_side == 'R': # 左方败北 - counter_stats[left_monster]['losses'] += 1 - - # 右方怪物的统计 - counter_stats[right_monster]['total_matchups'] += 1 - if victory_side == 'L': # 右方败北 - counter_stats[right_monster]['losses'] += 1 - - # 计算被克制率 - countered_results = [] - for monster_id, stats in counter_stats.items(): - if stats['total_matchups'] >= 20: # 至少20场对战 - loss_rate = stats['losses'] / stats['total_matchups'] - monster_name = get_monster_name(monster_id) - countered_results.append({ - '怪物': monster_name, - '怪物ID': monster_id, - '被克制率': loss_rate, - '败场': stats['losses'], - '总对战数': stats['total_matchups'] - }) - - # 按被克制率排序(降序) - countered_results.sort(key=lambda x: -x['被克制率']) - return pd.DataFrame(countered_results[:5]) - - -def analyze_individual_monster_relations(df): - """分析每个怪物的详细关系:最佳队友、克制关系、被克制关系""" - monster_relations = {} - - # 初始化数据结构 - single_stats = defaultdict(lambda: {'appearances': 0, 'wins': 0}) - pair_stats = defaultdict(lambda: {'co_occurrences': 0, 'co_wins': 0}) - counter_stats = defaultdict(lambda: defaultdict(lambda: {'matchups': 0, 'wins': 0})) - - for _, record in df.iterrows(): - victory_side = record['Result'] - - # 获取左右两方的怪物 - left_monsters = [] - right_monsters = [] - - for i in range(1, MONSTER_COUNT + 1): - try: - left_val = float(record[f'L{i}']) if pd.notna(record[f'L{i}']) else 0 - right_val = float(record[f'R{i}']) if pd.notna(record[f'R{i}']) else 0 - - if left_val > 0: - left_monsters.append(i) - if right_val > 0: - right_monsters.append(i) - except (ValueError, TypeError): - continue - - # 确定胜利队伍和失败队伍 - if victory_side == 'L': - win_team, lose_team = left_monsters, right_monsters - else: - win_team, lose_team = right_monsters, left_monsters - - # 更新单怪统计 - for monster in win_team: - single_stats[monster]['appearances'] += 1 - single_stats[monster]['wins'] += 1 - - for monster in lose_team: - single_stats[monster]['appearances'] += 1 - - # 更新队友统计 - for i in range(len(win_team)): - for j in range(i+1, len(win_team)): - x, y = sorted((win_team[i], win_team[j])) - pair_stats[(x, y)]['co_occurrences'] += 1 - pair_stats[(x, y)]['co_wins'] += 1 - - for i in range(len(lose_team)): - for j in range(i+1, len(lose_team)): - x, y = sorted((lose_team[i], lose_team[j])) - pair_stats[(x, y)]['co_occurrences'] += 1 - - # 更新克制关系统计 - for winner in win_team: - for loser in lose_team: - counter_stats[winner][loser]['matchups'] += 1 - counter_stats[winner][loser]['wins'] += 1 - counter_stats[loser][winner]['matchups'] += 1 - - # 为每个怪物分析关系 - for monster_id in range(1, MONSTER_COUNT + 1): - monster_name = get_monster_name(monster_id) - - if single_stats[monster_id]['appearances'] < 10: # 数据量太少 - continue - - # 分析最佳队友 - best_teammates = [] - for (x, y), stats in pair_stats.items(): - if x == monster_id or y == monster_id: - partner_id = y if x == monster_id else x - if stats['co_occurrences'] >= 5: # 至少5次合作 - combo_win_rate = stats['co_wins'] / stats['co_occurrences'] - - # 计算提升度 - if (single_stats[monster_id]['appearances'] > 0 and - single_stats[partner_id]['appearances'] > 0): - monster_win_rate = single_stats[monster_id]['wins'] / single_stats[monster_id]['appearances'] - partner_win_rate = single_stats[partner_id]['wins'] / single_stats[partner_id]['appearances'] - expected_win_rate = sqrt(monster_win_rate * partner_win_rate) - - if expected_win_rate > 0: - lift = combo_win_rate / expected_win_rate - if lift > 1.0: - best_teammates.append({ - 'partner_id': partner_id, - 'partner_name': get_monster_name(partner_id), - 'lift': lift, - 'combo_win_rate': combo_win_rate, - 'occurrences': stats['co_occurrences'] - }) - - best_teammates.sort(key=lambda x: -x['lift']) - - # 分析克制关系 - counters = [] # 该怪物克制的 - countered_by = [] # 克制该怪物的 - - for opponent_id in counter_stats[monster_id]: - stats = counter_stats[monster_id][opponent_id] - if stats['matchups'] >= 5: - win_rate = stats['wins'] / stats['matchups'] - if win_rate > 0.6: # 胜率超过60%认为克制 - counters.append({ - 'opponent_id': opponent_id, - 'opponent_name': get_monster_name(opponent_id), - 'win_rate': win_rate, - 'matchups': stats['matchups'] - }) - - for opponent_id in range(1, MONSTER_COUNT + 1): - if opponent_id in counter_stats and monster_id in counter_stats[opponent_id]: - stats = counter_stats[opponent_id][monster_id] - if stats['matchups'] >= 5: - lose_rate = stats['wins'] / stats['matchups'] - if lose_rate > 0.6: # 对方胜率超过60%认为被克制 - countered_by.append({ - 'opponent_id': opponent_id, - 'opponent_name': get_monster_name(opponent_id), - 'lose_rate': lose_rate, - 'matchups': stats['matchups'] - }) - - counters.sort(key=lambda x: -x['win_rate']) - countered_by.sort(key=lambda x: -x['lose_rate']) - - monster_relations[monster_id] = { - 'name': monster_name, - 'best_teammates': best_teammates[:3], - 'counters': counters[:3], - 'countered_by': countered_by[:3] - } - - return monster_relations - - -def get_terrain_feature_columns(): - """获取地形特征列名""" - import json - import re - from collections import defaultdict - - try: - # 加载类别映射 - class_map_path = "tools/battlefield_recognize/class_to_idx.json" - with open(class_map_path, 'r', encoding='utf-8') as f: - class_to_idx = json.load(f) - - # 使用与data_cleaning_with_field_recognize_gpu.py相同的逻辑 - grouped_elements = defaultdict(list) - for class_name in class_to_idx.keys(): - if class_name.endswith('_none'): - continue - condensed_name = re.sub(r'_left_', '_', class_name) - condensed_name = re.sub(r'_right_', '_', condensed_name) - grouped_elements[condensed_name].append(class_name) - - # 返回排序后的特征列名 - return sorted(grouped_elements.keys()) - except Exception as e: - print(f"无法获取地形特征列名,使用默认值: {e}") - # 如果无法获取,返回默认列表 - return [ - "altar_vertical_altar", "block_parallel_block", "block_vertical_altar_shape1", - "block_vertical_altar_shape2", "block_vertical_block_shape1", "block_vertical_block_shape2", - "coil_narrow_coil", "coil_wide_coil", "crossbow_top_crossbow", - "fire_side_crossbow", "fire_side_fire", "fire_top_fire" - ] - - -def analyze_terrain_effects(df): - """分析地形对怪物的影响""" - terrain_effects = [] - - # 获取实际的地形特征列名 - terrain_feature_columns = get_terrain_feature_columns() - - # 地形显示名称映射 - terrain_display_mapping = { - "altar_vertical_altar": "垂直祭坛", - "block_parallel_block": "平行方块阻挡", - "block_vertical_altar_shape1": "垂直祭坛形阻挡1", - "block_vertical_altar_shape2": "垂直祭坛形阻挡2", - "block_vertical_block_shape1": "垂直方块阻挡1", - "block_vertical_block_shape2": "垂直方块阻挡2", - "coil_narrow_coil": "窄型线圈装置", - "coil_wide_coil": "宽型线圈装置", - "crossbow_top_crossbow": "顶部弩炮", - "fire_side_crossbow": "侧边弩炮", - "fire_side_fire": "侧边火炮", - "fire_top_fire": "顶部火炮" - } - - for terrain_idx, terrain_key in enumerate(terrain_feature_columns): - terrain_name = terrain_display_mapping.get(terrain_key, terrain_key) - - for monster_idx in range(1, MONSTER_COUNT + 1): - monster_name = get_monster_name(monster_idx) - - # 转换数据类型 - df[f'FL{terrain_idx+1}'] = pd.to_numeric(df[f'FL{terrain_idx+1}'], errors='coerce').fillna(0) - df[f'FR{terrain_idx+1}'] = pd.to_numeric(df[f'FR{terrain_idx+1}'], errors='coerce').fillna(0) - df[f'L{monster_idx}'] = pd.to_numeric(df[f'L{monster_idx}'], errors='coerce').fillna(0) - df[f'R{monster_idx}'] = pd.to_numeric(df[f'R{monster_idx}'], errors='coerce').fillna(0) - - # 有地形时的表现 - terrain_left_games = df[(df[f'FL{terrain_idx+1}'] == 1) & (df[f'L{monster_idx}'] > 0)] - terrain_right_games = df[(df[f'FR{terrain_idx+1}'] == 1) & (df[f'R{monster_idx}'] > 0)] - - terrain_total = len(terrain_left_games) + len(terrain_right_games) - if terrain_total < 5: # 数据量太少 - continue - - terrain_wins = len(terrain_left_games[terrain_left_games['Result'] == 'L']) + \ - len(terrain_right_games[terrain_right_games['Result'] == 'R']) - terrain_win_rate = terrain_wins / terrain_total - - # 无地形时的表现 - normal_left_games = df[(df[f'FL{terrain_idx+1}'] == 0) & (df[f'L{monster_idx}'] > 0)] - normal_right_games = df[(df[f'FR{terrain_idx+1}'] == 0) & (df[f'R{monster_idx}'] > 0)] - - normal_total = len(normal_left_games) + len(normal_right_games) - if normal_total < 5: - continue - - normal_wins = len(normal_left_games[normal_left_games['Result'] == 'L']) + \ - len(normal_right_games[normal_right_games['Result'] == 'R']) - normal_win_rate = normal_wins / normal_total - - # 计算影响程度 - effect = terrain_win_rate - normal_win_rate - - if abs(effect) >= 0.05: # 胜率差异超过5%才记录 - terrain_effects.append({ - '地形': terrain_name, - '怪物': monster_name, - '怪物ID': monster_idx, - '地形胜率': terrain_win_rate, - '普通胜率': normal_win_rate, - '影响程度': effect, - '地形场次': terrain_total, - '普通场次': normal_total - }) - - # 按影响程度绝对值排序 - terrain_effects.sort(key=lambda x: -abs(x['影响程度'])) - return pd.DataFrame(terrain_effects[:20]) # 增加到前20个 - - -def analyze_device_counter_effects(df): - """分析五个装置对怪物的克制效果""" - device_counter_results = {} - - # 获取实际的地形特征列名 - terrain_feature_columns = get_terrain_feature_columns() - - # 定义五个装置类别及其对应的地形特征 - device_categories = { - 'altar': { - 'name': '祭坛', - 'features': [f for f in terrain_feature_columns if 'altar' in f], - 'description': '祭坛类装置' - }, - 'block': { - 'name': '箱子/阻挡', - 'features': [f for f in terrain_feature_columns if 'block' in f], - 'description': '方块阻挡类装置' - }, - 'coil': { - 'name': '电桩', - 'features': [f for f in terrain_feature_columns if 'coil' in f], - 'description': '线圈电桩装置' - }, - 'crossbow': { - 'name': '弩箭', - 'features': [f for f in terrain_feature_columns if 'crossbow' in f], - 'description': '弩炮装置' - }, - 'fire': { - 'name': '火炮', - 'features': [f for f in terrain_feature_columns if 'fire' in f], - 'description': '火炮装置' - } - } - - for device_key, device_info in device_categories.items(): - device_name = device_info['name'] - device_features = device_info['features'] - - if not device_features: - continue - - device_effects = [] - - # 对每个怪物分析该装置的克制效果 - for monster_idx in range(1, MONSTER_COUNT + 1): - monster_name = get_monster_name(monster_idx) - - # 收集该装置所有特征的统计数据 - total_device_games = 0 - total_device_wins = 0 - total_normal_games = 0 - total_normal_wins = 0 - - for terrain_key in device_features: - if terrain_key not in terrain_feature_columns: - continue - - terrain_idx = terrain_feature_columns.index(terrain_key) - - # 转换数据类型 - df[f'FL{terrain_idx+1}'] = pd.to_numeric(df[f'FL{terrain_idx+1}'], errors='coerce').fillna(0) - df[f'FR{terrain_idx+1}'] = pd.to_numeric(df[f'FR{terrain_idx+1}'], errors='coerce').fillna(0) - df[f'L{monster_idx}'] = pd.to_numeric(df[f'L{monster_idx}'], errors='coerce').fillna(0) - df[f'R{monster_idx}'] = pd.to_numeric(df[f'R{monster_idx}'], errors='coerce').fillna(0) - - # 有该装置时的表现 - device_left_games = df[(df[f'FL{terrain_idx+1}'] == 1) & (df[f'L{monster_idx}'] > 0)] - device_right_games = df[(df[f'FR{terrain_idx+1}'] == 1) & (df[f'R{monster_idx}'] > 0)] - - device_games_count = len(device_left_games) + len(device_right_games) - device_wins_count = len(device_left_games[device_left_games['Result'] == 'L']) + \ - len(device_right_games[device_right_games['Result'] == 'R']) - - # 无该装置时的表现 - normal_left_games = df[(df[f'FL{terrain_idx+1}'] == 0) & (df[f'L{monster_idx}'] > 0)] - normal_right_games = df[(df[f'FR{terrain_idx+1}'] == 0) & (df[f'R{monster_idx}'] > 0)] - - normal_games_count = len(normal_left_games) + len(normal_right_games) - normal_wins_count = len(normal_left_games[normal_left_games['Result'] == 'L']) + \ - len(normal_right_games[normal_right_games['Result'] == 'R']) - - total_device_games += device_games_count - total_device_wins += device_wins_count - total_normal_games += normal_games_count - total_normal_wins += normal_wins_count - - # 计算整体效果 - if total_device_games >= 10 and total_normal_games >= 10: # 确保有足够的数据 - device_win_rate = total_device_wins / total_device_games - normal_win_rate = total_normal_wins / total_normal_games - effect = device_win_rate - normal_win_rate - - # 计算克制程度(负值表示被该装置克制) - counter_effect = -effect # 装置对怪物的克制效果 - - if abs(effect) >= 0.05: # 胜率差异超过5%才记录 - device_effects.append({ - '怪物': monster_name, - '怪物ID': monster_idx, - '装置胜率': device_win_rate, - '普通胜率': normal_win_rate, - '克制程度': counter_effect, # 正值表示被该装置克制 - '装置场次': total_device_games, - '普通场次': total_normal_games, - '效果类型': '被克制' if counter_effect > 0 else '克制装置' - }) - - # 按克制程度排序(被克制程度最高的在前) - device_effects.sort(key=lambda x: -x['克制程度']) - device_counter_results[device_key] = { - 'name': device_name, - 'description': device_info['description'], - 'features': device_features, - 'effects': device_effects[:10] # 取前10个被克制最严重的怪物 - } - - return device_counter_results - - -def create_html_table(df, columns, title, is_combo=False, monster_relations=None): - """创建带有怪物头像的HTML表格""" - html = f"

{title}

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

装置克制效果统计

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

{device_name}({device_description})

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

明日方舟争锋频道绿藤城

-
-

数据概览:

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

装置克制效果统计

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

{device_name}({device_description})

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

明日方舟争锋频道绿藤城

+
+

数据概览:

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

所有怪物胜率排行榜

暂无数据

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

最佳怪物配合

暂无足够的配合数据

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

地形影响

暂无足够的地形数据

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

装置克制效果统计

暂无足够的装置数据

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

报告生成时间:{timestamp}

+

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

+
+ + +""" + + # 保存报告 + with open("comprehensive_monster_report.html", "w", encoding="utf-8") as f: + f.write(html) + + print("统计报告已生成:comprehensive_monster_report.html") + + # 也输出到控制台 + print("\n=== 怪物胜率TOP10 ===") + print(win_rates.head(10).to_string()) + + if not combinations.empty: + print("\n=== 最佳配合TOP10 ===") + print( + combinations.head(10)[ + ["组合", "提升度", "组合胜率", "出场次数"] + ].to_string() + ) + + # 输出装置克制效果 + if device_counter_effects: + print("\n=== 装置克制效果统计 ===") + for device_key, device_data in device_counter_effects.items(): + if device_data["effects"]: + print( + f"\n{device_data['name']}({device_data['description']})最克制的怪物TOP5:" + ) + for i, effect in enumerate(device_data["effects"][:5]): + print( + f" {i+1}. {effect['怪物']} - 克制程度: {effect['克制程度']:.2%} ({effect['效果类型']})" + ) + + if not terrain_effects.empty: + print("\n=== 地形影响TOP10 ===") + print( + terrain_effects.head(10)[ + ["地形", "怪物", "影响程度", "地形胜率", "普通胜率"] + ].to_string() + ) + + +if __name__ == "__main__": + generate_comprehensive_report() diff --git "a/data_train/package/\346\225\260\346\215\256\345\214\205\346\224\276\345\234\250\346\255\244\350\267\257\345\276\204.md" b/src/core/__init__.py similarity index 100% rename from "data_train/package/\346\225\260\346\215\256\345\214\205\346\224\276\345\234\250\346\255\244\350\267\257\345\276\204.md" rename to src/core/__init__.py diff --git a/config.py b/src/core/config.py similarity index 54% rename from config.py rename to src/core/config.py index 31acae5..819cea0 100644 --- a/config.py +++ b/src/core/config.py @@ -1,13 +1,14 @@ -from pathlib import Path import cv2 import numpy as np import pandas as pd import logging +from .paths import DATA_DIR, IMAGES_DIR + logger = logging.getLogger(__name__) -# 全局地形特征数量常量 -FIELD_FEATURE_COUNT = 0 # 默认值 +FIELD_FEATURE_COUNT = 0 + def load_images() -> dict[str, np.ndarray]: """ @@ -15,13 +16,13 @@ def load_images() -> dict[str, np.ndarray]: returns: dict - 图片字典,键为文件名(不含扩展名),值为numpy.ndarray对象 """ images = {} - images_path = Path('images') - # 遍历images目录下的所有文件 - for image_file in images_path.glob('*.*'): - if image_file.suffix.lower() in ('.png', '.jpg', '.jpeg', '.bmp'): + images_path = IMAGES_DIR + for image_file in images_path.glob("*.*"): + if image_file.suffix.lower() in (".png", ".jpg", ".jpeg", ".bmp"): try: - # img = cv2.imread(str(image_file), cv2.IMREAD_COLOR) - img = cv2.imdecode(np.fromfile(image_file, dtype=np.uint8), cv2.IMREAD_COLOR) + img = cv2.imdecode( + np.fromfile(image_file, dtype=np.uint8), cv2.IMREAD_COLOR + ) if img is None: logger.error(f"无法加载图片: {image_file}") continue @@ -30,13 +31,19 @@ def load_images() -> dict[str, np.ndarray]: logger.error(f"加载图片出错 {image_file}: {str(e)}") return images + MONSTER_IMAGES = load_images() + def load_monster_data(): - monster_data = pd.read_csv('monster_greenvine.csv', index_col="id", encoding='utf-8-sig') + monster_data = pd.read_csv( + DATA_DIR / "monster_greenvine.csv", + index_col="id", + encoding="utf-8-sig", + ) return monster_data + MONSTER_DATA = load_monster_data() -# 全局变量 -MONSTER_COUNT = len(MONSTER_DATA) # 根据怪物数据自动设置数量 +MONSTER_COUNT = len(MONSTER_DATA) diff --git a/constants.py b/src/core/constants.py similarity index 89% rename from constants.py rename to src/core/constants.py index 60d3adc..a1a2590 100644 --- a/constants.py +++ b/src/core/constants.py @@ -7,10 +7,10 @@ "health": 1390, "magic_resist": 0, "attack_interval": 3.3, - "move_speed": 1 / 2, # 除2是因为方舟就是要除2 + "move_speed": 1 / 2, # 除2是因为方舟就是要除2 "attack_radius": 2.75, "effect": "破甲15", - "icon": "images/1.png" + "icon": "", }, 2: { "name": "灼热源石虫", @@ -23,7 +23,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "images/2.png" + "icon": "", }, 3: { "name": "狂暴的猎狗pro", @@ -36,7 +36,7 @@ "move_speed": 1.9 / 2, "attack_radius": 0.8, "effect": "", - "icon": "images/3.png" + "icon": "", }, 4: { "name": "炮击组长", @@ -49,7 +49,7 @@ "move_speed": 0.8 / 2, "attack_radius": 7, "effect": "溅射", - "icon": "images/4.png" + "icon": "", }, 5: { "name": "萨卡兹大剑手", @@ -62,7 +62,7 @@ "move_speed": 0.85 / 2, "attack_radius": 0.8, "effect": "", - "icon": "images/5.png" + "icon": "", }, 6: { "name": "宿主流浪者", @@ -75,7 +75,7 @@ "move_speed": 0.65 / 2, "attack_radius": 0.8, "effect": "再生", - "icon": "images/6.png" + "icon": "", }, 7: { "name": "重装防御者", @@ -88,7 +88,7 @@ "move_speed": 0.75 / 2, "attack_radius": 0.8, "effect": "", - "icon": "images/7.png" + "icon": "", }, 8: { "name": "复仇者", @@ -101,7 +101,7 @@ "move_speed": 0.65 / 2, "attack_radius": 0.8, "effect": "火刀", - "icon": "images/8.png" + "icon": "", }, 9: { "name": "狂暴宿主组长", @@ -114,7 +114,7 @@ "move_speed": 1.2 / 2, "attack_radius": 0.8, "effect": "掉血", - "icon": "images/9.png" + "icon": "", }, 10: { "name": "巧克力流心虫虫", @@ -127,7 +127,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "images/2.png" + "icon": "", }, 11: { "name": "巧克力流心虫虫", @@ -140,7 +140,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "images/2.png" + "icon": "", }, 12: { "name": "泥岩巨像", @@ -153,7 +153,7 @@ "move_speed": 0.4 / 2, "attack_radius": 0.8, "effect": "", - "icon": "images/12.png" + "icon": "", }, 13: { "name": "高塔术师", @@ -166,7 +166,7 @@ "move_speed": 0.6 / 2, "attack_radius": 3.2, "effect": "打2 溅射", - "icon": "images/13.png" + "icon": "", }, 14: { "name": "巧克力流心虫虫", @@ -179,7 +179,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "images/2.png" + "icon": "", }, 15: { "name": "巧克力流心虫虫", @@ -192,7 +192,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "images/2.png" + "icon": "", }, 16: { "name": "冰原术师", @@ -205,7 +205,7 @@ "move_speed": 0.7 / 2, "attack_radius": 3.2, "effect": "打2 寒冷", - "icon": "images/16.png" + "icon": "", }, 17: { "name": "巧克力流心虫虫", @@ -218,7 +218,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "images/2.png" + "icon": "", }, 18: { "name": "弧光锋卫长", @@ -231,7 +231,7 @@ "move_speed": 0.75 / 2, "attack_radius": 0.8, "effect": "", - "icon": "images/18.png" + "icon": "", }, 19: { "name": "巧克力流心虫虫", @@ -244,7 +244,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "images/2.png" + "icon": "", }, 20: { "name": "提亚卡乌好战者", @@ -257,7 +257,7 @@ "move_speed": 0.8 / 2, "attack_radius": 0.8, "effect": "破甲10", - "icon": "images/20.png" + "icon": "", }, 21: { "name": "矿脉守卫", @@ -270,7 +270,7 @@ "move_speed": 0.6 / 2, "attack_radius": 0.8, "effect": "反伤", - "icon": "images/21.png" + "icon": "", }, 22: { "name": "巧克力流心虫虫", @@ -283,7 +283,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "images/2.png" + "icon": "", }, 23: { "name": "巧克力流心虫虫", @@ -296,7 +296,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "images/2.png" + "icon": "", }, 24: { "name": "巧克力流心虫虫", @@ -309,7 +309,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "images/2.png" + "icon": "", }, 25: { "name": "巧克力流心虫虫", @@ -322,7 +322,7 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "images/2.png" + "icon": "", }, 26: { "name": "巧克力流心虫虫", @@ -335,6 +335,6 @@ "move_speed": 0.8 / 2, "attack_radius": 2.8, "effect": "灼燃", - "icon": "images/2.png" - } -} \ No newline at end of file + "icon": "", + }, +} diff --git a/src/core/paths.py b/src/core/paths.py new file mode 100644 index 0000000..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