feat.完成自动抄作业-部署干员全部逻辑

This commit is contained in:
MistEO
2022-03-22 21:20:46 +08:00
parent b9f0e1ac40
commit a9ff54a3d6
6 changed files with 163 additions and 72 deletions

View File

@@ -45,6 +45,9 @@
],
"direction": 1
},
{
"type": 100
},
{
"name": "临光",
"location": [

View File

@@ -3265,6 +3265,16 @@
60
]
},
"BattleSpeedUp": {
"algorithm": "JustReturn",
"action": "ClickRect",
"specificRect": [
1070,
20,
60,
60
]
},
"BattlePause": {
"algorithm": "JustReturn",
"action": "ClickRect",
@@ -3307,6 +3317,16 @@
255
]
},
"BattleCostData": {
"algorithm": "OcrDetect",
"text": [],
"roi": [
1197,
492,
83,
48
]
},
"BattleUseOper": {
"algorithm": "JustReturn",
"preDelay": 500,

View File

@@ -42,7 +42,15 @@ bool asst::BattleImageAnalyzer::analyze()
ret &= kills_analyze();
}
return true;
if (m_target & Target::Cost) {
ret &= cost_analyze();
}
if (m_target & Target::Vacancies) {
ret &= vacancies_analyze();
}
return ret;
}
const std::vector<asst::BattleRealTimeOper>& asst::BattleImageAnalyzer::get_opers() const noexcept
@@ -108,7 +116,7 @@ bool asst::BattleImageAnalyzer::opers_analyze()
oper.role = oper_role_analyze(role_rect);
Rect cost_rect = flag_mrect.rect.move(cost_move);
oper.cost = oper_cost_analyze(cost_rect);
//oper.cost = oper_cost_analyze(cost_rect);
oper.index = index++;
m_opers.emplace_back(std::move(oper));
@@ -381,7 +389,7 @@ bool asst::BattleImageAnalyzer::kills_analyze()
// 这里的结果应该是 "击杀数/总的敌人数",例如 "0/41"
std::string kills_res = kills_analyzer.get_result().front().text;
int pos = kills_res.find('/');
size_t pos = kills_res.find('/');
if (pos == std::string::npos) {
return false;
}
@@ -400,7 +408,24 @@ bool asst::BattleImageAnalyzer::kills_analyze()
bool asst::BattleImageAnalyzer::cost_analyze()
{
return false;
OcrImageAnalyzer cost_analyzer(m_image);
cost_analyzer.set_task_info("BattleKillsFlag");
if (!cost_analyzer.analyze()) {
return false;
}
cost_analyzer.sort_result_by_score();
std::string cost_str = cost_analyzer.get_result().front().text;
if (cost_str.empty() ||
!std::all_of(cost_str.cbegin(), cost_str.cend(),
[](char c) -> bool {return std::isdigit(c);})) {
return false;
}
m_cost = std::stoi(cost_str);
return true;
}
bool asst::BattleImageAnalyzer::vacancies_analyze()

View File

@@ -8,15 +8,16 @@ namespace asst
class BattleImageAnalyzer : public AbstractImageAnalyzer
{
public:
enum Target // 需要识别的目标
enum Target // 需要识别的目标
{
HP, // 剩余生命值
Home, // 蓝色的家门
Oper, // 下方的干员信息
Skill, // cd 转好了可以使用的技能
Kills, // 击杀数
Cost, // 费用
Vacancies, // 剩余可部署干员数
None = 0,
HP = 1, // 剩余生命值
Home = 2, // 蓝色的家门
Oper = 4, // 下方的干员信息
Skill = 8, // cd 转好了可以使用的技能
Kills = 16, // 击杀数
Cost = 32, // 费用
Vacancies = 64, // 剩余可部署干员数
// 肉鸽模式需要用到的识别
Roguelike = Home | HP | Oper | Skill
};
@@ -55,6 +56,7 @@ namespace asst
std::vector<Rect> m_ready_skills; // 可以释放的技能Rect实际是干员的位置
int m_hp = 0; // 剩余生命值
int m_kills = 0; // 击杀数
int m_cost = 0; // 部署费用
protected:
// 该分析器不支持外部设置ROI

View File

@@ -28,7 +28,7 @@ bool asst::BattleProcessTask::_run()
;
}
for (const BattleAction& action : m_actions.actions) {
for (const auto& action : m_actions.actions) {
do_action(action);
}
@@ -59,18 +59,18 @@ bool asst::BattleProcessTask::get_stage_info()
bool asst::BattleProcessTask::analyze_opers_preview()
{
BattleImageAnalyzer analyzer(m_ctrler->get_image());
analyzer.set_target(BattleImageAnalyzer::Target::Oper);
if (!analyzer.analyze()) {
BattleImageAnalyzer oper_analyzer(m_ctrler->get_image());
oper_analyzer.set_target(BattleImageAnalyzer::Target::Oper);
if (!oper_analyzer.analyze()) {
return false;
}
// TODO: 干员头像出来之后,还要过 2 秒左右才可以点击,这里可能还要加个延时
battle_pause();
auto opers = analyzer.get_opers();
auto opers = oper_analyzer.get_opers();
for (size_t i = 0; i != opers.size(); ++i) {
const auto& cur_oper = analyzer.get_opers();
const auto& cur_oper = oper_analyzer.get_opers();
size_t offset = opers.size() > cur_oper.size() ? opers.size() - cur_oper.size() : 0;
m_ctrler->click(cur_oper.at(i - offset).rect);
@@ -95,8 +95,8 @@ bool asst::BattleProcessTask::analyze_opers_preview()
m_opers_info.emplace(std::move(oper_name), std::move(opers.at(i)));
// 干员特别多的时候,任意干员被点开,都会导致下方的干员图标被裁剪和移动。所以这里需要重新识别一下
analyzer.set_image(image);
analyzer.analyze();
oper_analyzer.set_image(image);
oper_analyzer.analyze();
}
battle_pause();
cancel_selection();
@@ -112,10 +112,6 @@ bool asst::BattleProcessTask::update_opers_info()
return false;
}
const auto& cur_opers_info = analyzer.get_opers();
// 干员数量没有变化,无需更新
if (cur_opers_info.size() == m_opers_info.size()) {
return true;
}
decltype(m_opers_info) pre_opers_info;
m_opers_info.swap(pre_opers_info);
@@ -124,12 +120,12 @@ bool asst::BattleProcessTask::update_opers_info()
for (const auto& cur_oper : cur_opers_info) {
int left_index = 0;
int right_index = 0;
// 干员数变多了,干员可能位于 [之前的位置, 之前的位置 + |size_change|] 之间
// 干员数变多了,之前的干员可能位于 [当前位置 - |size_change|, 当前位置 ] 之间
if (is_increased) {
left_index = static_cast<int>(cur_oper.index);
right_index = static_cast<int>(cur_oper.index) + size_change;
left_index = static_cast<int>(cur_oper.index) - size_change;
right_index = static_cast<int>(cur_oper.index);
}
// 干员数减少了,干员可能位于 [之前的位置 - |size_change|, 之前的位置] 之间
// 干员数减少了,之前的干员可能位于 [当前位置 , 当前位置 + |size_change|] 之间
else {
left_index = static_cast<int>(cur_oper.index);
right_index = static_cast<int>(cur_oper.index) - size_change; // size_change 是 负值
@@ -143,25 +139,34 @@ bool asst::BattleProcessTask::update_opers_info()
ranged_iters.emplace_back(iter);
}
}
MatchImageAnalyzer avatar_analyzer(cur_oper.avatar);
avatar_analyzer.set_task_info("BattleAvatarData");
decltype(ranged_iters)::value_type matched_iter;
MatchRect matched_result;
// 遍历比较,得分最高的那个就说明是对应的那个
for (const auto& iter : ranged_iters) {
avatar_analyzer.set_templ(iter->second.avatar);
if (!avatar_analyzer.analyze()) {
continue;
}
if (matched_result.score < avatar_analyzer.get_result().score) {
matched_result = avatar_analyzer.get_result();
matched_iter = iter;
decltype(ranged_iters)::value_type matched_iter = ranged_iters.front();
if (ranged_iters.empty()) {
Log.error("ranged_iters empty", left_index, right_index);
return false;
}
else if (ranged_iters.size() > 1) {
MatchImageAnalyzer avatar_analyzer(cur_oper.avatar);
avatar_analyzer.set_task_info("BattleAvatarData");
MatchRect matched_result;
// 遍历比较,得分最高的那个就说明是对应的那个
for (const auto& iter : ranged_iters) {
avatar_analyzer.set_templ(iter->second.avatar);
if (!avatar_analyzer.analyze()) {
continue;
}
if (matched_result.score < avatar_analyzer.get_result().score) {
matched_result = avatar_analyzer.get_result();
matched_iter = iter;
}
}
}
if (matched_result.score == 0) {
continue;
else {
; // 只有一个干员的时候就不用计算,就是唯一的那个
}
auto temp_oper = cur_oper;
temp_oper.name = matched_iter->first;
// 保存当前干员信息
@@ -175,19 +180,65 @@ bool asst::BattleProcessTask::do_action(const BattleAction& action)
if (!wait_condition(action)) {
return false;
}
while (!update_opers_info()) {
;
switch (action.type) {
case BattleActionType::Deploy:
return oper_deploy(action);
break;
case BattleActionType::Retreat:
break;
case BattleActionType::UseSkill:
break;
case BattleActionType::SwitchSpeed:
return battle_speedup();
break;
case BattleActionType::SlowMode:
break;
}
return false;
}
bool asst::BattleProcessTask::wait_condition(const BattleAction& action)
{
if (action.kills_condition <= 0) {
return true;
}
constexpr int analyze_target = BattleImageAnalyzer::Target::Kills;
while (true) {
auto image = m_ctrler->get_image();
BattleImageAnalyzer analyzer(image);
analyzer.set_target(analyze_target);
if (!analyzer.analyze()) {
return true;
}
if (action.kills_condition <= analyzer.get_kills()) {
return true;
}
}
return false;
}
bool asst::BattleProcessTask::oper_deploy(const BattleAction& action)
{
const auto use_oper_task_ptr = Task.get("BattleUseOper");
const auto swipe_oper_task_ptr = Task.get("BattleSwipeOper");
// TODO临时调试方案。正式版本这里需要对 group_name -> oper_name 做一个转换
auto iter = m_opers_info.find(action.group_name);
if (iter == m_opers_info.cend()) {
Log.error("battle opers group", action.group_name, "not found");
return false;
}
decltype(m_opers_info)::iterator iter;
do {
update_opers_info();
// TODO临时调试方案。正式版本这里需要对 group_name -> oper_name 做一个转换
iter = m_opers_info.find(action.group_name);
if (iter == m_opers_info.cend()) {
Log.error("battle opers group", action.group_name, "not found");
return false;
}
} while (!iter->second.available);
// 点击干员
Rect oper_rect = iter->second.rect;
@@ -201,6 +252,7 @@ bool asst::BattleProcessTask::do_action(const BattleAction& action)
sleep(use_oper_task_ptr->rear_delay);
// 拖动干员朝向
if (action.direction != BattleDeployDirection::No) {
static const std::unordered_map<BattleDeployDirection, Point> DirectionMapping = {
{ BattleDeployDirection::Right, Point(1, 0)},
@@ -210,7 +262,7 @@ bool asst::BattleProcessTask::do_action(const BattleAction& action)
{ BattleDeployDirection::No, Point(0, 0)},
};
// 计算往哪边拖动(干员朝向)
// 计算往哪边拖动
Point direction = DirectionMapping.at(action.direction);
// 将方向转换为实际的 swipe end 坐标点
@@ -231,31 +283,16 @@ bool asst::BattleProcessTask::do_action(const BattleAction& action)
return true;
}
bool asst::BattleProcessTask::wait_condition(const BattleAction& action)
{
constexpr int analyze_target = BattleImageAnalyzer::Target::Kills | BattleImageAnalyzer::Target::Vacancies | BattleImageAnalyzer::Target::Cost;
while (true) {
auto image = m_ctrler->get_image();
BattleImageAnalyzer analyzer(image);
analyzer.set_target(analyze_target);
if (!analyzer.analyze()) {
return true;
}
if (action.kills_condition <= analyzer.get_kills()) {
return true;
}
}
return false;
}
bool asst::BattleProcessTask::battle_pause()
{
return ProcessTask(*this, { "BattlePause" }).run();
}
bool asst::BattleProcessTask::battle_speedup()
{
return ProcessTask(*this, { "BattleSpeedUp" }).run();
}
bool asst::BattleProcessTask::cancel_selection()
{
return ProcessTask(*this, { "BattleCancelSelection" }).run();

View File

@@ -21,6 +21,7 @@ namespace asst
bool get_stage_info();
bool battle_pause();
bool battle_speedup();
bool cancel_selection(); // 取消选择干员
bool analyze_opers_preview();
bool update_opers_info();
@@ -28,12 +29,15 @@ namespace asst
bool do_action(const BattleAction& action);
bool wait_condition(const BattleAction& action);
bool oper_deploy(const BattleAction& action);
std::string m_stage_name;
std::unordered_map<Point, TilePack::TileInfo> m_side_tile_info;
std::unordered_map<Point, TilePack::TileInfo> m_normal_tile_info;
BattleActionsGroup m_actions;
/* 实时更新的数据 */
std::unordered_map<std::string, BattleRealTimeOper> m_opers_info;
};
}