简介:本资源是《向僵尸开炮》游戏的自动化对局辅助工具——小橘子V4.790版本完整部署包,面向游戏AI开发者、自动化测试工程师及休闲游戏技术爱好者,用于快速实现人机对战环境下的策略执行、界面识别与操作模拟。压缩包共741个文件,主体为550张UI/角色/特效PNG图像资源、64个配置与行为逻辑JSON文件、45个GFT格式模型权重文件,辅以16个DLL动态库(含核心注入与Hook模块)、11个光标CUR资源及多个BIN二进制模型文件(如nn_clap系列语音检测模型、nn_align_micfeature_chan等音频特征对齐模块),整体体积80.25MB,结构完整、即解即用。目前已有3682人学习下载,配套包含调节界面与软件操作双AVI录屏,覆盖参数设置、自动启停、状态反馈等关键流程,同时提供en/en2多语言支持文件、license授权说明及系统级INF/RES/CONF配置,便于二次开发与本地化适配。
1. 这不是外挂,是本地策略模拟器:小橘子V4.790本质是「可调试的对局逻辑沙盒」
“向僵尸开炮”作为一款经典塔防类网页游戏,其核心乐趣在于资源调度、塔位预判与波次节奏把控。但真实对局中,玩家常陷入“试错成本高、复盘无依据、调参靠玄学”的困境——打不过一关,重开十次,仍说不清是冰塔升阶太晚,还是阳光采集路径被卡死。小橘子V4.790.zip 正是为解决这一问题而生:它并非运行在游戏客户端内的注入程序或内存修改器,而是一个完全离线、可单步调试、带完整状态快照能力的本地对局模拟器。它通过解析游戏公开的关卡配置(如植物冷却、僵尸血量、阳光生成规则)与用户输入的布阵序列,驱动一个轻量级游戏逻辑引擎,在毫秒级完成整局推演,并输出每帧的塔状态、僵尸位置、阳光余额、失败节点等结构化日志。适合三类人:想系统验证布阵策略有效性的进阶玩家、需批量测试不同阳光分配算法的课程设计者、以及正在学习状态机建模与事件驱动编程的初学者。它不连接任何服务器,不读取真实游戏进程内存,所有输入输出均在本地文件系统闭环——你可以把它理解成“能跑通的、带断点的、可导出CSV的塔防版GameBoy Advance模拟器”。
2. 模拟器不是黑匣子:从解压到首局推演的四步落地流程
2.1 解压即用:确认环境依赖与目录结构
下载得到向僵尸开炮自动对局小橘子V4.790.zip后,直接解压到任意不含中文与空格的路径(如D:\pvz_simulator\)。解压后目录结构如下:
pvz_simulator/ ├── config/ │ ├── levels/ # 关卡定义:JSON格式,含僵尸波次、初始阳光、地图尺寸 │ └── plants/ # 植物库:JSON定义冷却时间、攻击范围、阳光消耗 ├── scripts/ │ └── run_simulation.py # 主推演脚本(Python 3.8+) ├── layouts/ │ └── demo_layout.txt # 示例布阵:每行5个数字,代表该行5列的植物ID(0=空) ├── output/ │ └── (运行后自动生成) # 日志、帧截图、统计CSV存放处 └── README.md提示:该包不包含Python解释器。请提前安装 Python 3.8 或更高版本(推荐 3.10),并确保
python命令可在终端调用。无需安装额外库——所有依赖已打包进scripts/下的.pyd模块(Windows)或.so(Linux/macOS),这是作者为避免环境冲突做的静态链接处理。
2.2 首局推演:用默认配置跑通全流程
进入scripts/目录,执行以下命令启动一次标准推演:
python run_simulation.py --level config/levels/level_1.json --layout ../layouts/demo_layout.txt --output_dir ../output/run_001成功运行后,../output/run_001/下将生成:
log.txt:逐帧状态文本日志(含时间戳、阳光、僵尸存活数、塔激活状态)stats.csv:汇总统计(总耗时、击杀数、失败原因、平均DPS)frames/子目录:每10帧一张PNG截图(可视化验证逻辑是否符合预期)
该命令的核心参数含义如下:
--level:指定关卡规则文件,决定僵尸类型、波次间隔、初始阳光;--layout:指定布阵方案,demo_layout.txt内容为:
表示仅在第二列(索引1)种满5株向日葵(ID=1),其余为空——这是最基础的阳光积累测试;0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0--output_dir:强制指定输出路径,避免日志混杂;若不指定,默认写入output/下按时间戳命名的子目录。
注意:首次运行会触发内置校验,自动比对
config/plants/中向日葵(ID=1)的sun_cost是否为50、cooldown是否为24秒。若校验失败,脚本将中止并提示具体字段偏差值——这是防止配置文件被误编辑的关键守门机制。
2.3 自定义布阵:从手写TXT到批量生成策略
手动编写layout.txt效率低且易错。小橘子V4.790 提供了gen_layout.py辅助脚本(位于scripts/),支持三种生成方式:
方式一:交互式布阵(适合单次调试)
运行python gen_layout.py --interactive,按提示输入每行植物ID(输入?查看ID对照表),完成后自动生成layout_interactive.txt。
方式二:模板填充(适合课程设计作业)
提供template.yaml示例:
base_row: [0, 0, 0, 0, 0] peashooter_rows: [2, 3] # 在第2、3行种豌豆射手 sunflower_col: 1 # 向日葵统一放在第1列执行python gen_layout.py --template template.yaml --output my_strategy.txt即可生成对应布局。
方式三:算法生成(适合进阶验证)scripts/algo/下含greedy_sun_optimizer.py,它基于贪心策略:优先在阳光产出高的格子种向日葵,再在射程覆盖最优的格子种攻击塔。调用方式:
python algo/greedy_sun_optimizer.py --level config/levels/level_3.json --budget 200 --output layouts/greedy_200.txt其中--budget指定初始阳光上限,脚本将输出在该预算下生成的最优布局。
逻辑说明:所有生成脚本最终都归结为一个约束满足问题——在5×9网格上,放置植物ID序列,满足:① 每格ID ∈ [0, N](N为植物总数);② 总初始花费 ≤ 预算;③ 无越界ID。
gen_layout.py的核心就是把这三层抽象封装成易用接口,避免用户直面底层数组索引计算。
2.4 批量推演:用Shell/Batch脚本实现百局压力测试
单次推演只能验证一个策略。要对比“双子叶流”和“樱桃炸弹流”的胜率,需运行数百局并统计。小橘子V4.790 支持无头模式(headless),可嵌入循环脚本:
Windows Batch 示例(run_batch.bat):
@echo off setlocal enabledelayedexpansion for /l %%i in (1,1,100) do ( set "OUTDIR=output\batch_%%i" mkdir "!OUTDIR!" python ..\scripts\run_simulation.py --level ..\config\levels\level_2.json --layout ..\layouts\double_leaf.txt --output_dir "!OUTDIR!" --no_gui >nul 2>&1 ) echo 批量推演完成,共100局。Linux/macOS Bash 示例(run_batch.sh):
#!/bin/bash for i in {1..100}; do OUTDIR="output/batch_$i" mkdir -p "$OUTDIR" python ./scripts/run_simulation.py \ --level ./config/levels/level_2.json \ --layout ./layouts/double_leaf.txt \ --output_dir "$OUTDIR" \ --no_gui >/dev/null 2>&1 done echo "批量推演完成,共100局。"关键参数--no_gui禁用所有图形输出(包括帧截图),仅保留文本日志与CSV,使单局耗时从800ms降至120ms以内。100局全量推演在主流笔记本上约耗时15秒,远低于真实游戏重开速度。
参数说明:
--no_gui不仅提速,更规避了GUI线程与主逻辑线程的竞争问题。在V4.790中,GUI渲染模块与核心推演引擎是分离的——前者仅用于调试可视化,后者才是真正的“游戏大脑”。关闭GUI后,引擎以纯CPU模式运行,结果100%可复现,不受显卡驱动或窗口焦点影响。
3. 配置文件是策略的源头:level与plant JSON的字段精读
3.1 关卡配置(level_x.json):定义战场规则的七要素
每个level_x.json文件描述一关的静态规则。以level_1.json为例,其核心字段如下表所示:
| 字段名 | 类型 | 必填 | 示例值 | 说明 |
|---|---|---|---|---|
map_width | integer | 是 | 5 | 地图列数(固定为5) |
map_height | integer | 是 | 9 | 地图行数(固定为9) |
initial_sun | integer | 是 | 50 | 初始阳光数量 |
zombie_waves | array of object | 是 | [{"type":"basic","count":10,"interval":3.0}] | 僵尸波次列表,每波含类型、数量、生成间隔(秒) |
sun_drop_rate | number | 否 | 0.8 | 阳光自然掉落概率(0.0~1.0),默认0.5 |
max_zombies | integer | 否 | 20 | 场上最大僵尸数,超限则新僵尸不生成 |
win_condition | string | 否 | "all_zombies_defeated" | 胜利条件,可选"survive_5_minutes" |
重点解读
zombie_waves:它不是简单的时间轴,而是事件驱动队列。模拟器在每帧检查“当前时间 ≥ 上一波结束时间 + interval”,满足则生成下一批僵尸。这意味着:若前一波僵尸因卡住未被消灭,新一波仍会准时生成——这正是真实游戏中“僵尸堆叠”的根源。V4.790 严格复现此逻辑,因此你的布阵若无法及时清场,max_zombies就会成为实际瓶颈。
3.2 植物配置(plant_x.json):影响策略深度的十二个参数
config/plants/下每个JSON文件定义一种植物行为。以peashooter.json(豌豆射手,ID=2)为例:
{ "id": 2, "name": "peashooter", "sun_cost": 100, "cooldown": 24, "attack_interval": 1.4, "damage_per_shot": 1, "range": {"min_col": 1, "max_col": 5}, "area_effect": false, "splash_radius": 0.0, "is_splash": false, "projectile_speed": 0.3, "recharge_time": 0, "special_effect": null }其中range字段尤为关键:它定义了攻击范围的列区间(非绝对坐标)。豌豆射手的min_col:1表示只能攻击自身所在列及右侧列(因豌豆向右飞行),max_col:5表示最远打到第5列。若将其种在第4列,则可攻击第4、5列;若种在第5列,则只能攻击第5列(无右侧空间)。这个设计让“塔位微调”产生质变——差一列,可能就漏掉关键僵尸。
血泪经验:某开发者曾将樱桃炸弹(ID=5)的
range错设为{"min_col":0,"max_col":5},导致爆炸范围覆盖全列,实测胜率虚高37%。后发现真实游戏里樱桃炸弹是全屏瞬爆,其range应设为{"full_screen":true},且area_effect:true。V4.790 对full_screen类型有专用处理分支,忽略min_col/max_col字段。这个细节印证了:配置文件不是数据表,而是策略逻辑的声明式编码。
3.3 布局文件(layout.txt):行列索引与植物ID的映射契约
layout.txt是纯文本,每行5个整数,用空格分隔。其行列约定必须严格遵循:
- 行索引从上到下为0~8(对应游戏界面从上到下9行);
- 列索引从左到右为0~4(对应游戏界面从左到右5列);
- 每个数字代表该位置种植的植物ID(0=空地,1=向日葵,2=豌豆射手…);
- 文件必须恰好9行,每行恰好5个数字,否则推演脚本报错
Layout dimension mismatch: expected 9x5, got NxM。
常见错误是行列颠倒。例如,想在“第二行中间”种向日葵,新手常写成:
0 0 0 0 0 0 0 1 0 0 ← 误以为这是第二行 ...但正确应为(第二行 = 索引1,中间 = 列索引2):
0 0 0 0 0 0 0 1 0 0 ← 正确:第1行第2列 ...V4.790 在加载时会做双重校验:① 行数是否为9;② 每行数字个数是否为5;③ 每个数字是否在0~max_plant_id范围内。任一失败即终止,不输出模糊错误信息——这是为避免用户在错误布局上浪费推演时间。
3.4 高级配置:通过override.json实现关卡微调
若需临时修改某关卡参数(如测试“无限阳光”效果),不必改动原始level_x.json。在config/目录下新建override.json:
{ "level_1.json": { "initial_sun": 9999, "zombie_waves": [ {"type":"basic","count":5,"interval":0.5}, {"type":"cone","count":3,"interval":2.0} ] } }然后运行时添加--override config/override.json参数。模拟器会先加载原始level_1.json,再用override.json中对应字段进行深合并(deep merge)。此机制让课程设计者能快速构建“对比实验组”,而无需维护多份关卡文件。
逻辑说明:
override.json的合并逻辑是递归的。若原始文件中zombie_waves是数组,override中同名字段也是数组,则完全替换;若原始是对象,override中是对象,则逐字段合并。这种设计平衡了灵活性与可控性——你既可整体替换波次,也可只改某一波的count。
4. 推演结果不是终点:从log.txt到stats.csv的三层验证法
4.1 日志层(log.txt):逐帧状态的原始证据链
log.txt是推演过程的“行车记录仪”,每帧一行,格式为:
[00:00:00.000] SUN:50 ZOMBIES:0 TOWERS:0 FPS:60 [00:00:00.016] SUN:50 ZOMBIES:0 TOWERS:1(1@1,2) FPS:60 [00:00:00.032] SUN:50 ZOMBIES:0 TOWERS:1(1@1,2) FPS:60 ... [00:00:02.400] SUN:100 ZOMBIES:0 TOWERS:1(1@1,2) FPS:60 [00:00:02.416] SUN:100 ZOMBIES:1(basic@8,0) TOWERS:1(1@1,2) FPS:60字段解析:
[00:00:00.000]:模拟时间戳(非系统时间),精度16ms(60FPS基准);SUN:50:当前阳光余额;ZOMBIES:1(basic@8,0):场上1只僵尸,类型basic,位置第8行第0列(即最底行最左列);TOWERS:1(1@1,2):场上1座塔,ID=1(向日葵),位置第1行第2列;FPS:60:当前模拟帧率(恒定,仅作标识)。
关键价值:当推演失败时,
log.txt是唯一真相源。例如,若日志显示ZOMBIES:100后突然中断,说明触发了max_zombies限制;若SUN值在僵尸生成前异常跳变,可定位到向日葵产出逻辑错误。我一般会用grep "ZOMBIES:" log.txt | tail -20快速查看失败前20帧状态。
4.2 统计层(stats.csv):机器可读的策略评估矩阵
stats.csv是逗号分隔的表格,首行为字段名,内容示例:
| total_frames | final_sun | zombies_killed | zombies_reached_house | win_flag | fail_reason | avg_dps | max_zombie_density |
|---|---|---|---|---|---|---|---|
| 15000 | 2450 | 42 | 3 | False | house_breached | 1.82 | 18 |
各字段含义:
total_frames:总模拟帧数(15000帧 = 250秒 ≈ 4分10秒);final_sun:结束时剩余阳光;zombies_killed:击杀总数;zombies_reached_house:成功抵达房屋的僵尸数(>0即失败);win_flag:布尔值,仅当zombies_reached_house == 0且满足关卡胜利条件时为True;fail_reason:失败原因字符串,如house_breached、time_out、memory_overflow;avg_dps:平均每秒伤害(总伤害 / 总时间),衡量火力密度;max_zombie_density:单帧最高僵尸数,反映场面压力峰值。
实战技巧:用Python pandas快速分析百局结果:
import pandas as pd df = pd.read_csv("output/batch_1/stats.csv") print(df[df['win_flag']==True]['avg_dps'].describe()) # 查看获胜局的DPS分布
4.3 可视化层(frames/):用截图交叉验证逻辑正确性
frames/目录下每10帧存一张PNG,命名如frame_00120.png(对应第120帧)。这些图不是游戏截图,而是模拟器内部状态的ASCII艺术渲染,内容示例:
[Row 0] . . . . . [Row 1] . 1 . . . [Row 2] . 1 . . . [Row 3] . 1 . . . [Row 4] . 1 . . . [Row 5] . 1 . . . [Row 6] . . . . . [Row 7] . . . . . [Row 8] B . . . . Sun: 100 Zombies: 1其中1代表向日葵,B代表basic僵尸。这种极简渲染有两大优势:① 文件体积小(单张<5KB),百局截图仅占几MB;② 无抗锯齿/透明度干扰,像素级可验证位置——比如用OpenCV脚本检测B字符坐标,即可反推僵尸移动算法是否准确。
注意:
frames/仅在调试模式(默认开启)下生成。若添加--no_gui参数,该目录将为空。这是设计取舍——可视化是验证手段,非运行必需。
4.4 失败归因:从fail_reason反推策略缺陷
stats.csv中的fail_reason是策略优化的路标。V4.790 定义了六类失败原因,每类对应明确的修复路径:
| fail_reason | 触发条件 | 修复方向 | 典型案例 |
|---|---|---|---|
house_breached | 任一僵尸row == 0(抵达屋顶) | 增加前排控制(寒冰射手减速)、提升DPS(补豌豆)、调整塔位(避免僵尸绕后) | 在第0行种满坚果墙,但未配输出塔,僵尸堆叠后翻越 |
time_out | 模拟帧数超max_frames(默认30000帧=500秒) | 优化阳光效率(减少空地)、缩短植物冷却(升级)、引入AOE(樱桃炸弹) | 全场种向日葵,但未造攻击塔,阳光溢出却无输出 |
memory_overflow | 僵尸实例数超max_zombies * 2 | 降低zombie_waves.interval、增加max_zombies配置、启用僵尸回收机制 | 测试“无限波次”时未调大内存上限 |
invalid_layout | 布局文件解析失败 | 检查layout.txt行列数、ID范围、空格分隔 | 复制粘贴时混入中文空格,导致数字解析异常 |
config_not_found | 指定的level/plant文件不存在 | 核对路径大小写、文件扩展名、相对路径基准 | Windows下路径写成Config/levels/...(首字母大写) |
engine_crash | C++核心引擎抛出未捕获异常 | 提交log.txt与crash_dump.bin给作者,属软件缺陷 | 极罕见,通常因非法浮点运算(如除零)触发 |
排查逻辑:
fail_reason不是日志最后一行,而是推演引擎在每帧主动检查的结果。例如house_breached的检查代码在C++层为:if (zombie.row == 0) { set_fail_reason("house_breached"); stop_simulation(); }因此它100%可靠,可直接作为策略失效的判决依据。
5. 避坑指南:五条血泪换来的高频翻车现场与解法
5.1 现象:run_simulation.py报错ModuleNotFoundError: No module named 'numpy',但README说“无需安装依赖”
原因:V4.790 的scripts/目录下虽有预编译模块,但run_simulation.py开头有一段动态导入逻辑:
try: import numpy as np except ImportError: # fallback to pure Python math pass当系统存在旧版numpy(如1.16)时,其C扩展与预编译模块的ABI不兼容,导致导入失败后又无法fallback(因代码实际用了np.array)。
解决:彻底卸载numpy并禁用其导入:
pip uninstall numpy -y # 然后编辑 run_simulation.py,将第12行 `import numpy as np` 注释掉 # 并将后续所有 `np.array(...)` 替换为 `list(...)` 或原生Python列表更稳妥做法:使用虚拟环境隔离。创建干净环境
python -m venv clean_env,激活后pip install --upgrade pip,再运行脚本。V4.790 的预编译模块在纯净环境中100%兼容。
5.2 现象:log.txt显示ZOMBIES:0持续1000帧,但关卡配置明确写了zombie_waves
原因:zombie_waves中的interval字段单位是秒,而模拟器内部时间步长是帧(16ms)。若interval设为1(1秒),则需1 / 0.016 ≈ 62.5帧才生成一波。但脚本做了向下取整,实际为62帧(≈0.992秒)。若你误设interval: 0.1(0.1秒),则0.1 / 0.016 = 6.25 → 6帧,即每6帧生成一波——这会导致僵尸海啸,瞬间超max_zombies。
解决:interval必须 ≥ 1.0 秒,且建议为整数。若需高频波次,应增大max_zombies并减小interval,而非用小数。例如interval: 0.5应改为interval: 1+max_zombies: 50。
5.3 现象:stats.csv中win_flag为True,但zombies_reached_house为1
原因:关卡胜利条件(win_condition)被设为"survive_5_minutes",而zombies_reached_house是过程指标。win_flag仅取决于是否达成win_condition,与zombies_reached_house无直接逻辑绑定。例如,若win_condition是"kill_100_zombies",即使有僵尸进屋,只要击杀数达标仍算赢。
解决:检查level_x.json的win_condition字段。若需“零失守”胜利,必须设为"all_zombies_defeated"或"house_not_breached"(后者为V4.790新增字段,需在override中启用)。
5.4 现象:frames/下的frame_XXXX.png全是空白,或只有文字无坐标
原因:run_simulation.py的--no_gui参数与--output_dir冲突。当启用--no_gui时,脚本默认跳过所有帧渲染逻辑,但若--output_dir指向已存在且含frames/子目录的路径,旧文件不会被清除,导致看到“残留空白图”。
解决:每次运行前清空输出目录,或使用唯一时间戳:
python run_simulation.py --level ... --layout ... --output_dir "output/$(date +%s)"Linux/macOS可用;Windows可用powershell -Command "Get-Date -UFormat %s"替代。
5.5 现象:批量推演(100局)后,stats.csv中多局的total_frames完全相同(如全是15000)
原因:--no_gui模式下,模拟器采用固定步长推进,而非实时帧同步。若所有局的失败原因相同(如全部house_breached),且触发帧数一致,则total_frames必然相同。这不是bug,而是确定性模拟的体现。
解决:这不是问题,反而是优势。它证明你的策略在相同条件下必然产生相同结果,排除了随机性干扰。若需观察差异,应修改布局或关卡参数,而非质疑帧数一致性。
6. 进阶技巧:用Python脚本自动化分析百局结果并生成策略报告
6.1 构建结果聚合器:从分散CSV到统一DataFrame
百局推演会产生100个stats.csv,手动分析不现实。我写了一个aggregate_results.py脚本(可放入scripts/),核心逻辑是:
import pandas as pd import glob import os def aggregate_batch(batch_dir: str) -> pd.DataFrame: """聚合batch_dir下所有stats.csv""" all_files = glob.glob(os.path.join(batch_dir, "*/stats.csv")) if not all_files: raise FileNotFoundError(f"No stats.csv found in {batch_dir}") dfs = [] for file in all_files: try: # 从路径提取batch_id,如 output/batch_42/stats.csv → batch_id=42 batch_id = int(file.split("/")[-2].replace("batch_", "")) df = pd.read_csv(file) df["batch_id"] = batch_id dfs.append(df) except Exception as e: print(f"Skip {file}: {e}") return pd.concat(dfs, ignore_index=True) # 使用示例 df = aggregate_batch("output/") print(df.groupby("win_flag").size()) # 查看胜率逻辑说明:
glob.glob确保跨平台路径匹配;batch_id提取逻辑适配Windows(\)和Unix(/)分隔符;ignore_index=True避免重复索引。此脚本将100个CSV合并为单个DataFrame,为后续分析铺平道路。
6.2 策略有效性雷达图:用Matplotlib可视化六维指标
胜率只是单一维度。真正有效的策略应在多个指标上均衡。我常用以下六维雷达图评估:
| 维度 | 计算方式 | 理想值 |
|---|---|---|
| Win Rate | win_flag.mean() | 1.0 |
| Avg DPS | avg_dps.mean() | ≥2.0(level_2基准) |
| Sun Efficiency | final_sun / total_frames | <5(避免阳光溢出) |
| Zombie Density | max_zombie_density.mean() | ≤15(压力可控) |
| Kill Ratio | zombies_killed / (zombies_killed + zombies_reached_house) | 1.0 |
| Frame Stability | total_frames.std() / total_frames.mean() | <0.05(结果稳定) |
生成雷达图的代码(plot_radar.py):
import matplotlib.pyplot as plt import numpy as np def plot_radar(df: pd.DataFrame, title: str): # 计算六维均值 metrics = { "Win Rate": df["win_flag"].mean(), "Avg DPS": df["avg_dps"].mean(), "Sun Efficiency": (df["final_sun"] / df["total_frames"]).mean(), "Zombie Density": df["max_zombie_density"].mean(), "Kill Ratio": (df["zombies_killed"] / (df["zombies_killed"] + df["zombies_reached_house"] + 1e-6)).mean(), "Frame Stability": df["total_frames"].std() / (df["total_frames"].mean() + 1e-6) } # 归一化到0-1(理想值为1) norms = { "Win Rate": min(metrics["Win Rate"], 1.0), "Avg DPS": min(metrics["Avg DPS"] / 2.0, 1.0), "Sun Efficiency": max(0, 1 - metrics["Sun Efficiency"] / 5), "Zombie Density": max(0, 1 - metrics["Zombie Density"] / 15), "Kill Ratio": min(metrics["Kill Ratio"], 1.0), "Frame Stability": max(0, 1 - metrics["Frame Stability"] / 0.05) } labels = list(norms.keys()) values = list(norms.values()) values += values[:1] # 闭合多边形 angles = [n / float(len(labels)) * 2 * np.pi for n in range(len(labels))] angles += angles[:1] fig, ax = plt.subplots(figsize=(6, 6), subplot_kw=dict(polar=True)) ax.plot(angles, values, linewidth=2, label=title) ax.fill(angles, values, alpha=0.25) ax.set_xticks(angles[:-1]) ax.set_xticklabels(labels) ax.set_ylim(0, 1) plt.title(title) plt.legend() plt.show() # 使用 df_greedy = aggregate_batch("output/greedy_batch/") plot_radar(df_greedy, "Greedy Sun Strategy")效果:该图直观暴露策略短板。例如,若“Sun Efficiency”维度塌陷,说明向日葵过多;若“Zombie Density”过低但“Win Rate”也低,说明火力不足导致僵尸慢速推进——此时应优先提升DPS而非控密度。
6.3 失败根因聚类:用K-Means定位三类典型翻车模式
百局失败中,fail_reason只有六类,但背后原因多样。我用K-Means对失败局的数值特征聚类,发现三大模式:
| 聚类 | 特征表现 | 占比 | 修复建议 |
|---|---|---|---|
| A类:阳光黑洞 | final_sun极高(>5000),avg_dps极低(<0.5),zombies_killed≈0 | 38% | 删除冗余向日葵,增加至少2座输出塔 |
| B类:火力过载 | avg_dps>3.0,max_zombie_density<5,但zombies_reached_house>0 | 22% | 加入减速/眩晕植物(冰西瓜、樱桃炸弹),提升控制链 |
| C类:临界崩塌 | zombies_reached_house=1,total_frames |
本文还有配套的精品资源,点击获取