happy-birds-cracker/README.md

7.6 KiB
Raw Permalink Blame History

happy-birds-cracker

基于 MaaFramework滑动三消(消消乐)自动求解器

MaaFramework 负责 截屏 + 图像识别 + ADB 滑动控制;本项目负责把画面变成棋盘模型、 计算最优一步、再把交换映射回屏幕滑动。

⚠️ 仅供学习与离线/单机研究。请遵守目标游戏的服务条款,线上对战/带反作弊的游戏请勿使用。

设计要点

  • 网格动态可变,恒为矩形:尺寸由 GridConfig(rows, cols) 决定,换关卡只需改行列数 (或在识别阶段探测后填入),核心算法与尺寸无关。
  • 三种块类型CellKind,为后续编程预留 meta 扩展字段):
    • NORMAL 正常块:可交换,带 color
    • BRICK 砖块:不可移动的障碍,会打断连线。
    • EMPTY 空块/空洞:无方块,会打断连线。

目录结构

src/hbc/
  board.py       棋盘数据模型Cell / CellKind / Board动态矩形
  solver.py      三消求解:找匹配 + 遍历相邻交换搜索最优解
  config.py      GridConfig像素<->格子映射,可存 JSON/ 模板比对参数
  state.py       GameState识别与动作之间共享 Board / 当前关卡
  templates.py   模板图像库:子图比对识别,支持多种墙(推荐识别方式)
  recognizer.py  截图 -> Board可插拔分类器模板/颜色 + MaaFw 识别桩)
  level.py       关卡识别OCR 标题"关卡一" -> 关卡号 -> 载入对应布局
  calibrate.py   标定工具:框选棋盘生成布局、预览、导出格子图(窗口自适应屏幕)
  actuator.py    Swap -> ADB 滑动(纯函数 + MaaFw 自定义动作 SwapAction
  main.py        连接设备、加载资源、注册自定义模块、循环运行(支持 --dry-run
resource/
  pipeline/
    pipeline.json  MaaFramework 低代码流程:识别棋盘 -> 执行交换 -> 循环
templates/       模板库你标定后放入normal/ wall/ empty/
layouts/         各关布局level1.json level2.json ...(你标定后生成)
tests/
  test_solver.py       求解器与模型测试
  test_recognition.py  模板识别 + 标定 + 多关卡布局测试(合成图,无需真机)
  test_level.py        关卡号 OCR 文本解析测试

如何识别棋盘

本游戏网格逐关变大且含空格。布局按关卡分文件存在 layouts/ 文件夹下 layouts/level1.jsonlayouts/level2.json ...),每关只需标定一次。 标定窗口会自动缩放适配屏幕(截图比屏幕大也能完整框选)。

# 1) 框出棋盘 + 填该关行列数 -> 写入 layouts/level1.json弹窗自动缩放鼠标拖框回车确认
python -m hbc.calibrate roi --image src-images/level1.jpg --level 1 --rows 5 --cols 5
#   窗口仍太大可调上限:--max-display 700
#   无 GUI / 想直接给坐标:--box x,y,w,h按原图像素

# 2) 叠加网格预览,确认每个格子对齐(绿框=棋盘,橙线=网格,红点=格子中心)
python -m hbc.calibrate preview --image src-images/level1.jpg --level 1 --out preview_level1.png

# 3) 把每个格子切成小图,导出到 cells/,再人工分类到模板库
python -m hbc.calibrate export --image src-images/level1.jpg --level 1 --out cells/

# 后续关卡同理,换 --level 2/3/... 和对应行列数即可
python -m hbc.calibrate roi --image src-images/level2.jpg --level 2 --rows 6 --cols 6

运行时用 --level 选择关卡:python -m hbc.main --level 1

对齐技巧(棋盘四周有 padding

均匀切分基于你框的矩形,所以框要贴着棋子区域而不是外层面板: 从左上角棋子的左上角拖到右下角棋子的右下角(把面板留白排除在外), 再用 preview 看红点是否落在每个棋子中心,不对就重框。反复"框 → 预览"几次即可对齐。

运行时自动识别当前是第几关OCR

游戏顶部写着"关卡一"MaaFramework 自带 OCRPaddleOCR 的 ONNX 模型),运行时 可直接识别,不用你预先截图。流程:标定一次标题区域 ROI -> LevelRecognition level.pyOCR 标题 -> parse_cn_level 解析成关卡号 -> 自动载入 layouts/level{N}.json。 若你的 MaaFw 资源里没带 OCR 模型,去掉该节点、改用 --level 手动指定即可。

把导出的小图按类别放进模板库(文件名即类别):

templates/
  normal/red.png  green.png  blue.png ...   每种正常块一张(文件名->稳定颜色 id
  wall/stone.png  ice.png    chain.png ...   墙可以有多种,每种一张
  empty/hole.png                             空块/空洞

为什么用模板比对而不是纯颜色

MaaFramework 的颜色识别对光照/特效/相近色容易误判。本项目识别走子图比对 把每个格子缩放后与模板逐像素比相似度,取最高分。相似度对颜色和形状都敏感 (不像 TM_CCOEFF_NORMED 会忽略颜色),更适合消消乐。仍保留 ColorClassifier 作为兜底。

冰块 = 低置信度即判为墙

本游戏除正常生物/空格外只有一种特殊方块——冰块(冰下隐约有正常图案)。冰下图案 模糊,模板匹配置信度天然偏低,因此采用一条简单规则:凡是匹配不够自信的格子, 一律判为冰墙cell.meta['wall']='ice',不可交换、打断连线)。

冰墙不会被直接交换,但当它旁边发生消除时,游戏里冰会化掉、露出正常生物,下一帧重新 识别就变回可用的普通块,于是自然推进。这是有意的取舍:牺牲对"未登记新方块"的健壮性, 换取对冰块的零配置支持。阈值在 TemplateMatchConfig.score_threshold / empty_min_score,需要时可调。

新增正常生物

后续关卡出现新动物时,把它的一张干净小图放进 templates/normal/<名字>.png 即可 (每个不同文件名 = 一个独立可消除类型;如灰色魟鱼 stingray.png 与青色水母 jellyfish.png 是两种)。用 hbc.calibrate export 可批量导出格子图来挑样本。

多种墙(可选)

若以后遇到别的固定障碍,可在 templates/wall/ 放多张墙图(石墙、锁链……), 它们都识别为 CellKind.BRICK,种类记在 cell.meta['wall'],便于针对性写逻辑。

数据流

MaaFramework 截屏
   -> BoardRecognitionrecognizer: 按网格切格、判颜色/砖块/空块 -> Board
   -> SwapActionactuator: Solver.find_best_swap(board) -> 最优 Swap
   -> 通过控制器 post_swipe 滑动
   -> 循环

求解与控制是解耦的:board.py + solver.py 完全不依赖 MaaFramework / 真机,可独立测试。

快速开始

# 安装为可编辑包:之后在项目根目录任何位置都能用 hbc无需设置 PYTHONPATH 或 cd src
pip install -e .

# 离线验证求解器(不需要设备)
python -m pytest tests/ -q

# 连接设备后运行(需先标定 config 中的 ROI 与颜色,见下)
python -m hbc.main            # 自动探测第一个 adb 设备
python -m hbc.main 127.0.0.1:16384  # 或指定模拟器地址
python -m hbc.main --level 2  # 求解第二关

预留的扩展点

  • Cell.meta:放特殊方块(条状/炸弹/彩球)、墙的种类 wall、砖块血量、冰冻层数等。
  • Solverscorer自定义打分策略默认按消除格子数4/5 连额外加权)。
  • Solver._cells_cleared_by:补充"相邻砖块被消除破坏"等连锁规则。
  • GridConfig.rows/cols:动态调整网格尺寸(每关不同时重新标定/探测)。
  • recognizer 的分类器可插拔:默认模板库,必要时换 ColorClassifier 兜底。