From dc6c6c1d54dde4f253114bd82afce07d58febdba Mon Sep 17 00:00:00 2001 From: MistEO Date: Sun, 6 Feb 2022 23:55:36 +0800 Subject: [PATCH] =?UTF-8?q?refactor.=E9=87=8D=E6=9E=84=E8=82=89=E9=B8=BD?= =?UTF-8?q?=E6=88=98=E6=96=97=E5=B9=B2=E5=91=98=E6=9C=9D=E5=90=91=E7=9A=84?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MeoAssistant/Assistant.cpp | 6 +- src/MeoAssistant/AsstDef.h | 8 +- src/MeoAssistant/AsstInfrastDef.h | 216 +++++------ src/MeoAssistant/Controller.cpp | 2 - .../RoguelikeBattleTaskPlugin.cpp | 232 ++++++----- src/MeoAssistant/RoguelikeBattleTaskPlugin.h | 10 + src/MeoAssistant/TaskData.cpp | 360 +++++++++--------- src/MeoAssistant/TilePack.cpp | 10 +- src/MeoAssistant/TilePack.h | 18 +- 9 files changed, 460 insertions(+), 402 deletions(-) diff --git a/src/MeoAssistant/Assistant.cpp b/src/MeoAssistant/Assistant.cpp index ef4530f1dc..ccb55786fe 100644 --- a/src/MeoAssistant/Assistant.cpp +++ b/src/MeoAssistant/Assistant.cpp @@ -358,10 +358,8 @@ bool Assistant::append_debug() { constexpr static const char* DebugTaskChain = "Debug"; - auto debug_task_ptr = std::make_shared(task_callback, (void*)this, DebugTaskChain); - debug_task_ptr->set_tasks({ "Roguelike1Begin" }); - debug_task_ptr->regiseter_plugin(); - debug_task_ptr->regiseter_plugin(); + auto debug_task_ptr = std::make_shared(task_callback, (void*)this, DebugTaskChain); + debug_task_ptr->set_stage_name("暴君"); m_tasks_queue.emplace(debug_task_ptr); } diff --git a/src/MeoAssistant/AsstDef.h b/src/MeoAssistant/AsstDef.h index b5923e5e15..de26dced7e 100644 --- a/src/MeoAssistant/AsstDef.h +++ b/src/MeoAssistant/AsstDef.h @@ -8,6 +8,8 @@ #include #include +#define NOMINMAX + namespace json { class value; @@ -60,7 +62,7 @@ namespace asst int half_width_scale = static_cast(width * (1 - scale) / 2); int half_hight_scale = static_cast(height * (1 - scale) / 2); Rect dst(x + half_width_scale, y + half_hight_scale, - static_cast(width * scale), static_cast(height * scale)); + static_cast(width * scale), static_cast(height * scale)); if (dst.x < 0) { dst.x = 0; } @@ -132,7 +134,7 @@ namespace asst enum class AlgorithmType { - Invaild = -1, + Invalid = -1, JustReturn, MatchTemplate, OcrDetect, @@ -208,7 +210,7 @@ namespace asst virtual ~TaskInfo() = default; std::string name; // 任务名 AlgorithmType algorithm = // 图像算法类型 - AlgorithmType::Invaild; + AlgorithmType::Invalid; ProcessTaskAction action = // 要进行的操作 ProcessTaskAction::Invalid; std::vector next; // 下一个可能的任务(列表) diff --git a/src/MeoAssistant/AsstInfrastDef.h b/src/MeoAssistant/AsstInfrastDef.h index 8a1bd0d508..3ae7f2dae1 100644 --- a/src/MeoAssistant/AsstInfrastDef.h +++ b/src/MeoAssistant/AsstInfrastDef.h @@ -4,127 +4,127 @@ namespace asst { - namespace infrast - { - struct Facility - { - ::std::string id; - ::std::vector<::std::string> products; - int max_num_of_opers = 0; - }; + namespace infrast + { + struct Facility + { + ::std::string id; + ::std::vector<::std::string> products; + int max_num_of_opers = 0; + }; - enum class SmileyType - { - Invalid = -1, - Rest, // 休息完成,绿色笑脸 - Work, // 工作中,黄色笑脸 - Distract // 注意力涣散,红色哭脸 - }; - struct Smiley - { - SmileyType type = SmileyType::Invalid; - Rect rect; - }; - enum class Doing // 正在做什么 - { - Invalid = -1, - Nothing, // 什么都不在做,也不在休息也不在工作 - Resting, // 休息中 - Working // 工作中 - }; + enum class SmileyType + { + Invalid = -1, + Rest, // 休息完成,绿色笑脸 + Work, // 工作中,黄色笑脸 + Distract // 注意力涣散,红色哭脸 + }; + struct Smiley + { + SmileyType type = SmileyType::Invalid; + Rect rect; + }; + enum class Doing // 正在做什么 + { + Invalid = -1, + Nothing, // 什么都不在做,也不在休息也不在工作 + Resting, // 休息中 + Working // 工作中 + }; - struct Skill - { - ::std::string id; - ::std::string templ_name; - ::std::vector<::std::string> names; // 很多基建技能是一样的,就是名字不同。所以一个技能id可能对应多个名字 - ::std::string desc; - ::std::unordered_map<::std::string, double> efficient; // 技能效率,key:产品名(赤金、经验书等), value: 效率数值 - ::std::unordered_map<::std::string, ::std::string> efficient_regex; // 技能效率正则,key:产品名(赤金、经验书等), value: 效率正则。如不为空,会先对正则进行计算,再加上efficient里面的值 - int max_num = INT_MAX; // 最多选几个该技能 + struct Skill + { + ::std::string id; + ::std::string templ_name; + ::std::vector<::std::string> names; // 很多基建技能是一样的,就是名字不同。所以一个技能id可能对应多个名字 + ::std::string desc; + ::std::unordered_map<::std::string, double> efficient; // 技能效率,key:产品名(赤金、经验书等), value: 效率数值 + ::std::unordered_map<::std::string, ::std::string> efficient_regex; // 技能效率正则,key:产品名(赤金、经验书等), value: 效率正则。如不为空,会先对正则进行计算,再加上efficient里面的值 + int max_num = INT_MAX; // 最多选几个该技能 - bool operator==(const Skill& skill) const noexcept - { - return id == skill.id; - } - }; - } + bool operator==(const Skill& skill) const noexcept + { + return id == skill.id; + } + }; + } } namespace std { - template <> - class hash - { - public: - size_t operator()(const asst::infrast::Skill& skill) const - { - return ::std::hash()(skill.id); - } - }; + template <> + class hash + { + public: + size_t operator()(const asst::infrast::Skill& skill) const + { + return ::std::hash()(skill.id); + } + }; } namespace asst { - namespace infrast - { - struct Oper - { - ::std::string face_hash; // 有些干员的技能是完全一样的,做个hash区分一下不同干员 - ::std::string name_hash; // 预留 - Smiley smiley; - double mood_ratio = 0; // 心情进度条的百分比 - Doing doing = Doing::Invalid; - bool selected = false; // 干员是否已被选择(蓝色的选择框) - ::std::unordered_set skills; - Rect rect; - }; + namespace infrast + { + struct Oper + { + ::std::string face_hash; // 有些干员的技能是完全一样的,做个hash区分一下不同干员 + ::std::string name_hash; // 预留 + Smiley smiley; + double mood_ratio = 0; // 心情进度条的百分比 + Doing doing = Doing::Invalid; + bool selected = false; // 干员是否已被选择(蓝色的选择框) + ::std::unordered_set skills; + Rect rect; + }; - struct SkillsComb - { - SkillsComb() = default; - SkillsComb(std::unordered_set skill_vec) - { - skills = ::std::move(skill_vec); - for (const auto& s : skills) { - for (const auto& [key, value] : s.efficient) { - efficient[key] += value; - } - for (const auto& [key, reg] : s.efficient_regex) { - efficient_regex[key] += "+(" + reg + ")"; - } - } - } - bool operator==(const SkillsComb& rhs) const - { - return skills == rhs.skills; - } + struct SkillsComb + { + SkillsComb() = default; + SkillsComb(std::unordered_set skill_vec) + { + skills = ::std::move(skill_vec); + for (const auto& s : skills) { + for (const auto& [key, value] : s.efficient) { + efficient[key] += value; + } + for (const auto& [key, reg] : s.efficient_regex) { + efficient_regex[key] += "+(" + reg + ")"; + } + } + } + bool operator==(const SkillsComb& rhs) const + { + return skills == rhs.skills; + } - ::std::string desc; - ::std::unordered_set skills; - ::std::unordered_map efficient; - ::std::unordered_map efficient_regex; + ::std::string desc; + ::std::unordered_set skills; + ::std::unordered_map efficient; + ::std::unordered_map efficient_regex; - ::std::string name_hash; - bool hash_filter = false; - ::std::unordered_map possible_hashs; // 限定只允许某些hash匹配的某些干员。若hash不相同,即使技能匹配了也不可用。hashs若为空,则不生效 - }; - // 基建技能组 - struct SkillsGroup - { - ::std::string desc; // 文字介绍,实际不起作用 - ::std::unordered_map conditions; // 技能组合可用条件,例如:key 发电站数量,value 3 - ::std::vector necessary; // 必选技能。这里面的缺少任一,则该技能组合不可用 - ::std::vector optional; // 可选技能。 - bool allow_external = false; // 当干员数没满3个的时候,是否允许补充外部干员 - }; + ::std::string name_hash; + bool hash_filter = false; + ::std::unordered_map possible_hashs; // 限定只允许某些hash匹配的某些干员。若hash不相同,即使技能匹配了也不可用。hashs若为空,则不生效 + }; + // 基建技能组 + struct SkillsGroup + { + ::std::string desc; // 文字介绍,实际不起作用 + ::std::unordered_map conditions; // 技能组合可用条件,例如:key 发电站数量,value 3 + ::std::vector necessary; // 必选技能。这里面的缺少任一,则该技能组合不可用 + ::std::vector optional; // 可选技能。 + bool allow_external = false; // 当干员数没满3个的时候,是否允许补充外部干员 + }; - enum class WorkMode - { - Invaild = -1, - Gentle, // 温和换班模式:会对干员人数不满的设施进行换班,计算单设施内最优解,尽量不破坏原有的干员组合;即若设施内干员是满的,则不对该设施进行换班 - Aggressive, // 激进换班模式:会对每一个设施进行换班,计算单设施内最优解,但不会将其他设施中的干员替换过来;即按工作状态排序,仅选择前面的干员 - Extreme // 偏激换班模式:会对每一个设施进行换班,计算全局的单设施内最优解,为追求更高效率,会将其他设施内的干员也替换过来;即按技能排序,计算所有拥有该设施技能的干员效率,无论他在不在其他地方工作 - }; - } + enum class WorkMode + { + Invalid = -1, + Gentle, // 温和换班模式:会对干员人数不满的设施进行换班,计算单设施内最优解,尽量不破坏原有的干员组合;即若设施内干员是满的,则不对该设施进行换班 + Aggressive, // 激进换班模式:会对每一个设施进行换班,计算单设施内最优解,但不会将其他设施中的干员替换过来;即按工作状态排序,仅选择前面的干员 + Extreme // 偏激换班模式:会对每一个设施进行换班,计算全局的单设施内最优解,为追求更高效率,会将其他设施内的干员也替换过来;即按技能排序,计算所有拥有该设施技能的干员效率,无论他在不在其他地方工作 + }; + } } diff --git a/src/MeoAssistant/Controller.cpp b/src/MeoAssistant/Controller.cpp index 2af4694582..5e5f80a747 100644 --- a/src/MeoAssistant/Controller.cpp +++ b/src/MeoAssistant/Controller.cpp @@ -659,8 +659,6 @@ bool asst::Controller::screencap() bool asst::Controller::screencap(const std::string & cmd, DecodeFunc decode_func) { - LogTraceFunction; - auto& adb = m_emulator_info.adb; auto ret = call_command(cmd); diff --git a/src/MeoAssistant/RoguelikeBattleTaskPlugin.cpp b/src/MeoAssistant/RoguelikeBattleTaskPlugin.cpp index dbdbc97a09..e51ea1cf33 100644 --- a/src/MeoAssistant/RoguelikeBattleTaskPlugin.cpp +++ b/src/MeoAssistant/RoguelikeBattleTaskPlugin.cpp @@ -24,23 +24,30 @@ bool asst::RoguelikeBattleTaskPlugin::verify(AsstMsg msg, const json::value& det } } +void asst::RoguelikeBattleTaskPlugin::set_stage_name(std::string stage) +{ + m_stage_name = std::move(stage); +} + bool asst::RoguelikeBattleTaskPlugin::_run() { - clear(); - bool getted_info = get_stage_info(); speed_up(); - if (getted_info) { - while (!need_exit()) { - // 不在战斗场景,且已使用过了干员,说明已经打完了,就结束循环 - if (!auto_battle() && m_used_opers) { - break; - } + if (!getted_info) { + return true; + } + + while (!need_exit()) { + // 不在战斗场景,且已使用过了干员,说明已经打完了,就结束循环 + if (!auto_battle() && m_used_opers) { + break; } } + clear(); + return true; } @@ -48,39 +55,72 @@ bool asst::RoguelikeBattleTaskPlugin::get_stage_info() { LogTraceFunction; - const auto stage_name_task_ptr = Task.get("BattleStageName"); - sleep(stage_name_task_ptr->pre_delay); - const auto& tile = Resrc.tile(); bool calced = false; - constexpr int StageNameRetryTimes = 50; - for (int i = 0; i != StageNameRetryTimes; ++i) { - cv::Mat image = Ctrler.get_image(); - OcrImageAnalyzer name_analyzer(image); - name_analyzer.set_task_info(stage_name_task_ptr); - if (!name_analyzer.analyze()) { - continue; - } - for (const auto& tr : name_analyzer.get_result()) { - auto side_info = tile.calc(tr.text, true); - if (side_info.empty()) { + if (m_stage_name.empty()) { + const auto stage_name_task_ptr = Task.get("BattleStageName"); + sleep(stage_name_task_ptr->pre_delay); + + constexpr int StageNameRetryTimes = 50; + for (int i = 0; i != StageNameRetryTimes; ++i) { + cv::Mat image = Ctrler.get_image(); + OcrImageAnalyzer name_analyzer(image); + name_analyzer.set_task_info(stage_name_task_ptr); + if (!name_analyzer.analyze()) { continue; } - m_side_tile_info = std::move(side_info); - calced = true; - auto cb_info = basic_info_with_what("StageInfo"); - auto& details = cb_info["details"]; - details["name"] = tr.text; - details["size"] = m_side_tile_info.size(); - callback(AsstMsg::SubTaskExtraInfo, cb_info); - break; - } - if (calced) { - break; + + for (const auto& tr : name_analyzer.get_result()) { + auto side_info = tile.calc(tr.text, true); + if (side_info.empty()) { + continue; + } + m_side_tile_info = std::move(side_info); + m_stage_name = tr.text; + calced = true; + break; + } + if (calced) { + break; + } } } - if (!calced) { + else { + m_side_tile_info = tile.calc(m_stage_name, true); + calced = true; + } + + if (calced) { +#ifdef ASST_DEBUG + auto normal_tiles = tile.calc(m_stage_name, false); + cv::Mat draw = Ctrler.get_image(); + for (const auto& [point, info] : normal_tiles) { + using TileKey = TilePack::TileKey; + static const std::unordered_map TileKeyMapping = { + { TileKey::Invalid, "invalid" }, + { TileKey::Forbidden, "forbidden" }, + { TileKey::Wall, "wall" }, + { TileKey::Road, "road" }, + { TileKey::Home, "end" }, + { TileKey::EnemyHome, "start" }, + { TileKey::Floor, "floor" }, + { TileKey::Hole, "hole" }, + { TileKey::Telin, "telin" }, + { TileKey::Telout, "telout" } + }; + + cv::putText(draw, TileKeyMapping.at(info.key), cv::Point(info.pos.x, info.pos.y), 1, 1, cv::Scalar(0, 0, 255)); + } +#endif + + auto cb_info = basic_info_with_what("StageInfo"); + auto& details = cb_info["details"]; + details["name"] = m_stage_name; + details["size"] = m_side_tile_info.size(); + callback(AsstMsg::SubTaskExtraInfo, cb_info); + } + else { callback(AsstMsg::SubTaskExtraInfo, basic_info_with_what("StageInfoError")); } @@ -91,7 +131,6 @@ bool asst::RoguelikeBattleTaskPlugin::auto_battle() { LogTraceFunction; - using Role = asst::BattleImageAnalyzer::Role; using Oper = asst::BattleImageAnalyzer::Oper; BattleImageAnalyzer battle_analyzer(Ctrler.get_image()); @@ -201,69 +240,20 @@ bool asst::RoguelikeBattleTaskPlugin::auto_battle() Ctrler.swipe(opt_oper.rect, placed_rect, swipe_oper_task_ptr->pre_delay); sleep(use_oper_task_ptr->rear_delay); - // 拖动干员朝向 - if (m_cur_home_index >= m_homes.size()) { - m_cur_home_index = 0; - } - Point home_loc(5, 5); - if (m_cur_home_index < m_homes.size()) { - home_loc = m_homes.at(m_cur_home_index); - } - Point home_point = m_side_tile_info.at(home_loc).pos; - Rect home_rect(home_point.x, home_point.y, 1, 1); - - int dx = placed_loc.x - home_loc.x; - int dy = placed_loc.y - home_loc.y; + // 计算往哪边拖动(干员朝向) + Point direction = calc_direction(placed_loc, opt_oper.role); + // 将方向转换为实际的 swipe end 坐标点 + Point end_point = placed_point; constexpr int coeff = 500; - Point end_point; - switch (opt_oper.role) { - case Role::Medic: - { - if (std::abs(dx) <= std::abs(dy)) { - dx = 0; - } - else { - dy = 0; - } - end_point.x = placed_point.x - coeff * dx; - end_point.y = placed_point.y - coeff * dy; - } - break; - case Role::Support: - case Role::Pioneer: - case Role::Warrior: - case Role::Sniper: - case Role::Special: - case Role::Tank: - case Role::Caster: - case Role::Drone: - default: - { - if (std::abs(dx) < std::abs(dy)) { - dx = 0; - } - else { - dy = 0; - } - end_point.x = placed_point.x + coeff * dx; - end_point.y = placed_point.y + coeff * dy; - } - break; - } + end_point.x += direction.x * coeff; + end_point.y += direction.y * coeff; + + end_point.x = std::max(0, end_point.x); + end_point.x = std::min(end_point.x, WindowWidthDefault); + end_point.y = std::max(0, end_point.y); + end_point.y = std::min(end_point.y, WindowHeightDefault); - if (end_point.x < 0) { - end_point.x = 0; - } - else if (end_point.x >= WindowWidthDefault) { - end_point.x = WindowWidthDefault - 1; - } - if (end_point.y < 0) { - end_point.y = 0; - } - else if (end_point.y >= WindowHeightDefault) { - end_point.y = WindowHeightDefault - 1; - } Ctrler.swipe(placed_point, end_point, swipe_oper_task_ptr->rear_delay); m_used_tiles.emplace(placed_loc); @@ -298,6 +288,7 @@ void asst::RoguelikeBattleTaskPlugin::clear() m_pre_hp = 0; m_homes.clear(); m_cur_home_index = 0; + m_stage_name.clear(); m_side_tile_info.clear(); m_used_tiles.clear(); } @@ -359,3 +350,54 @@ asst::Point asst::RoguelikeBattleTaskPlugin::get_placed(Loc buildable_type) return nearest; } + +asst::Point asst::RoguelikeBattleTaskPlugin::calc_direction(Point loc, Role role) +{ + // 根据家门的方向计算一下大概的朝向 + if (m_cur_home_index >= m_homes.size()) { + m_cur_home_index = 0; + } + Point home_loc(5, 5); + if (m_cur_home_index < m_homes.size()) { + home_loc = m_homes.at(m_cur_home_index); + } + Point home_point = m_side_tile_info.at(home_loc).pos; + Rect home_rect(home_point.x, home_point.y, 1, 1); + + int dx = loc.x - home_loc.x; + int dy = loc.y - home_loc.y; + + Point direction(0, 0); + switch (role) { + case Role::Medic: + { + if (std::abs(dx) < std::abs(dy)) { + direction.y = -dy; + } + else { + direction.x = -dx; + } + } + break; + case Role::Support: + case Role::Pioneer: + case Role::Warrior: + case Role::Sniper: + case Role::Special: + case Role::Tank: + case Role::Caster: + case Role::Drone: + default: + { + if (std::abs(dx) < std::abs(dy)) { + direction.y = dy; + } + else { + direction.x = dx; + } + } + break; + } + + return direction; +} diff --git a/src/MeoAssistant/RoguelikeBattleTaskPlugin.h b/src/MeoAssistant/RoguelikeBattleTaskPlugin.h index 00fbde743c..3f43f35cf7 100644 --- a/src/MeoAssistant/RoguelikeBattleTaskPlugin.h +++ b/src/MeoAssistant/RoguelikeBattleTaskPlugin.h @@ -3,18 +3,22 @@ #include "AbstractTaskPlugin.h" #include "AsstDef.h" #include "TilePack.h" +#include "BattleImageAnalyzer.h" namespace asst { class RoguelikeBattleTaskPlugin : public AbstractTaskPlugin { using Loc = asst::TilePack::BuildableType; + using Role = asst::BattleImageAnalyzer::Role; public: using AbstractTaskPlugin::AbstractTaskPlugin; virtual ~RoguelikeBattleTaskPlugin() = default; virtual bool verify(AsstMsg msg, const json::value& details) const override; + void set_stage_name(std::string stage); + protected: virtual bool _run() override; @@ -28,6 +32,10 @@ namespace asst // 返回 可格子的位置 Point get_placed(Loc buildable_type); + // 计算摆放干员的朝向 + // 返回滑动的方向 + Point calc_direction(Point loc, Role role); + bool m_used_opers = false; int m_pre_hp = 0; @@ -35,5 +43,7 @@ namespace asst std::vector m_homes; size_t m_cur_home_index = 0; std::unordered_set m_used_tiles; + + std::string m_stage_name; }; } diff --git a/src/MeoAssistant/TaskData.cpp b/src/MeoAssistant/TaskData.cpp index 94e67c361e..27ed1cd47a 100644 --- a/src/MeoAssistant/TaskData.cpp +++ b/src/MeoAssistant/TaskData.cpp @@ -10,211 +10,211 @@ const std::shared_ptr asst::TaskData::get(const std::string& name) const noexcept { - if (auto iter = m_all_tasks_info.find(name); - iter != m_all_tasks_info.cend()) { - return iter->second; - } - else { - return nullptr; - } + if (auto iter = m_all_tasks_info.find(name); + iter != m_all_tasks_info.cend()) { + return iter->second; + } + else { + return nullptr; + } } const std::unordered_set& asst::TaskData::get_templ_required() const noexcept { - return m_templ_required; + return m_templ_required; } std::shared_ptr asst::TaskData::get(std::string name) { - return m_all_tasks_info[std::move(name)]; + return m_all_tasks_info[std::move(name)]; } void asst::TaskData::clear_cache() noexcept { - for (auto&& [name, ptr] : m_all_tasks_info) { - ptr->region_of_appeared = Rect(); - } + for (auto&& [name, ptr] : m_all_tasks_info) { + ptr->region_of_appeared = Rect(); + } } bool asst::TaskData::parse(const json::value& json) { - auto to_lower = [](char c) -> char { - return (c >= 'A' && c <= 'Z') ? (c - 'A' + 'a') : c; - }; - for (const auto& [name, task_json] : json.as_object()) { - std::string algorithm_str = task_json.get("algorithm", "matchtemplate"); - std::transform(algorithm_str.begin(), algorithm_str.end(), algorithm_str.begin(), to_lower); - AlgorithmType algorithm = AlgorithmType::Invaild; - if (algorithm_str == "matchtemplate") { - algorithm = AlgorithmType::MatchTemplate; - } - else if (algorithm_str == "justreturn") { - algorithm = AlgorithmType::JustReturn; - } - else if (algorithm_str == "ocrdetect") { - algorithm = AlgorithmType::OcrDetect; - } - else if (algorithm_str == "hash") { - algorithm = AlgorithmType::Hash; - } - else { - m_last_error = "algorithm error " + algorithm_str; - return false; - } + auto to_lower = [](char c) -> char { + return (c >= 'A' && c <= 'Z') ? (c - 'A' + 'a') : c; + }; + for (const auto& [name, task_json] : json.as_object()) { + std::string algorithm_str = task_json.get("algorithm", "matchtemplate"); + std::transform(algorithm_str.begin(), algorithm_str.end(), algorithm_str.begin(), to_lower); + AlgorithmType algorithm = AlgorithmType::Invalid; + if (algorithm_str == "matchtemplate") { + algorithm = AlgorithmType::MatchTemplate; + } + else if (algorithm_str == "justreturn") { + algorithm = AlgorithmType::JustReturn; + } + else if (algorithm_str == "ocrdetect") { + algorithm = AlgorithmType::OcrDetect; + } + else if (algorithm_str == "hash") { + algorithm = AlgorithmType::Hash; + } + else { + m_last_error = "algorithm error " + algorithm_str; + return false; + } - std::shared_ptr task_info_ptr = nullptr; - switch (algorithm) { - case AlgorithmType::JustReturn: - task_info_ptr = std::make_shared(); - break; - case AlgorithmType::MatchTemplate: { - auto match_task_info_ptr = std::make_shared(); - match_task_info_ptr->templ_name = task_json.get("template", name + ".png"); - m_templ_required.emplace(match_task_info_ptr->templ_name); + std::shared_ptr task_info_ptr = nullptr; + switch (algorithm) { + case AlgorithmType::JustReturn: + task_info_ptr = std::make_shared(); + break; + case AlgorithmType::MatchTemplate: { + auto match_task_info_ptr = std::make_shared(); + match_task_info_ptr->templ_name = task_json.get("template", name + ".png"); + m_templ_required.emplace(match_task_info_ptr->templ_name); - match_task_info_ptr->templ_threshold = task_json.get( - "templThreshold", TemplThresholdDefault); - match_task_info_ptr->special_threshold = task_json.get( - "specialThreshold", 0); - if (task_json.contains("maskRange")) { - match_task_info_ptr->mask_range = std::make_pair( - task_json.at("maskRange")[0].as_integer(), - task_json.at("maskRange")[1].as_integer()); - } + match_task_info_ptr->templ_threshold = task_json.get( + "templThreshold", TemplThresholdDefault); + match_task_info_ptr->special_threshold = task_json.get( + "specialThreshold", 0); + if (task_json.contains("maskRange")) { + match_task_info_ptr->mask_range = std::make_pair( + task_json.at("maskRange")[0].as_integer(), + task_json.at("maskRange")[1].as_integer()); + } - task_info_ptr = match_task_info_ptr; - } break; - case AlgorithmType::OcrDetect: { - auto ocr_task_info_ptr = std::make_shared(); - for (const json::value& text : task_json.at("text").as_array()) { - ocr_task_info_ptr->text.emplace_back(text.as_string()); - } - ocr_task_info_ptr->need_full_match = task_json.get("need_match", false); - if (task_json.contains("ocrReplace")) { - for (const json::value& rep : task_json.at("ocrReplace").as_array()) { - ocr_task_info_ptr->replace_map.emplace(rep.as_array()[0].as_string(), rep.as_array()[1].as_string()); - } - } - task_info_ptr = ocr_task_info_ptr; - } break; - case AlgorithmType::Hash: - { - auto hash_task_info_ptr = std::make_shared(); - for (const json::value& hash : task_json.at("hash").as_array()) { - hash_task_info_ptr->hashs.emplace_back(hash.as_string()); - } - hash_task_info_ptr->dist_threshold = task_json.get("threshold", 0); - if (task_json.contains("maskRange")) { - hash_task_info_ptr->mask_range = std::make_pair( - task_json.at("maskRange")[0].as_integer(), - task_json.at("maskRange")[1].as_integer()); - } - hash_task_info_ptr->bound = task_json.get("bound", true); + task_info_ptr = match_task_info_ptr; + } break; + case AlgorithmType::OcrDetect: { + auto ocr_task_info_ptr = std::make_shared(); + for (const json::value& text : task_json.at("text").as_array()) { + ocr_task_info_ptr->text.emplace_back(text.as_string()); + } + ocr_task_info_ptr->need_full_match = task_json.get("need_match", false); + if (task_json.contains("ocrReplace")) { + for (const json::value& rep : task_json.at("ocrReplace").as_array()) { + ocr_task_info_ptr->replace_map.emplace(rep.as_array()[0].as_string(), rep.as_array()[1].as_string()); + } + } + task_info_ptr = ocr_task_info_ptr; + } break; + case AlgorithmType::Hash: + { + auto hash_task_info_ptr = std::make_shared(); + for (const json::value& hash : task_json.at("hash").as_array()) { + hash_task_info_ptr->hashs.emplace_back(hash.as_string()); + } + hash_task_info_ptr->dist_threshold = task_json.get("threshold", 0); + if (task_json.contains("maskRange")) { + hash_task_info_ptr->mask_range = std::make_pair( + task_json.at("maskRange")[0].as_integer(), + task_json.at("maskRange")[1].as_integer()); + } + hash_task_info_ptr->bound = task_json.get("bound", true); - task_info_ptr = hash_task_info_ptr; - } break; - } - task_info_ptr->cache = task_json.get("cache", true); - task_info_ptr->algorithm = algorithm; - task_info_ptr->name = name; - std::string action = task_json.get("action", std::string()); - std::transform(action.begin(), action.end(), action.begin(), to_lower); - if (action == "clickself") { - task_info_ptr->action = ProcessTaskAction::ClickSelf; - } - else if (action == "clickrand") { - task_info_ptr->action = ProcessTaskAction::ClickRand; - } - else if (action == "donothing" || action.empty()) { - task_info_ptr->action = ProcessTaskAction::DoNothing; - } - else if (action == "stop") { - task_info_ptr->action = ProcessTaskAction::Stop; - } - else if (action == "clickrect") { - task_info_ptr->action = ProcessTaskAction::ClickRect; - const json::value& rect_json = task_json.at("specificRect"); - task_info_ptr->specific_rect = Rect( - rect_json[0].as_integer(), - rect_json[1].as_integer(), - rect_json[2].as_integer(), - rect_json[3].as_integer()); - } - else if (action == "swipetotheleft") { - task_info_ptr->action = ProcessTaskAction::SwipeToTheLeft; - } - else if (action == "swipetotheright") { - task_info_ptr->action = ProcessTaskAction::SwipeToTheRight; - } - else { - m_last_error = "Task: " + name + " error: " + action; - return false; - } + task_info_ptr = hash_task_info_ptr; + } break; + } + task_info_ptr->cache = task_json.get("cache", true); + task_info_ptr->algorithm = algorithm; + task_info_ptr->name = name; + std::string action = task_json.get("action", std::string()); + std::transform(action.begin(), action.end(), action.begin(), to_lower); + if (action == "clickself") { + task_info_ptr->action = ProcessTaskAction::ClickSelf; + } + else if (action == "clickrand") { + task_info_ptr->action = ProcessTaskAction::ClickRand; + } + else if (action == "donothing" || action.empty()) { + task_info_ptr->action = ProcessTaskAction::DoNothing; + } + else if (action == "stop") { + task_info_ptr->action = ProcessTaskAction::Stop; + } + else if (action == "clickrect") { + task_info_ptr->action = ProcessTaskAction::ClickRect; + const json::value& rect_json = task_json.at("specificRect"); + task_info_ptr->specific_rect = Rect( + rect_json[0].as_integer(), + rect_json[1].as_integer(), + rect_json[2].as_integer(), + rect_json[3].as_integer()); + } + else if (action == "swipetotheleft") { + task_info_ptr->action = ProcessTaskAction::SwipeToTheLeft; + } + else if (action == "swipetotheright") { + task_info_ptr->action = ProcessTaskAction::SwipeToTheRight; + } + else { + m_last_error = "Task: " + name + " error: " + action; + return false; + } - task_info_ptr->max_times = task_json.get("maxTimes", INT_MAX); - if (task_json.contains("exceededNext")) { - const json::array& excceed_next_arr = task_json.at("exceededNext").as_array(); - for (const json::value& excceed_next : excceed_next_arr) { - task_info_ptr->exceeded_next.emplace_back(excceed_next.as_string()); - } - } - else { - task_info_ptr->exceeded_next.emplace_back("Stop"); - } - task_info_ptr->pre_delay = task_json.get("preDelay", 0); - task_info_ptr->rear_delay = task_json.get("rearDelay", 0); - if (task_json.contains("reduceOtherTimes")) { - const json::array& reduce_arr = task_json.at("reduceOtherTimes").as_array(); - for (const json::value& reduce : reduce_arr) { - task_info_ptr->reduce_other_times.emplace_back(reduce.as_string()); - } - } - if (task_json.contains("roi")) { - const json::array& area_arr = task_json.at("roi").as_array(); - int x = area_arr[0].as_integer(); - int y = area_arr[1].as_integer(); - int width = area_arr[2].as_integer(); - int height = area_arr[3].as_integer(); + task_info_ptr->max_times = task_json.get("maxTimes", INT_MAX); + if (task_json.contains("exceededNext")) { + const json::array& excceed_next_arr = task_json.at("exceededNext").as_array(); + for (const json::value& excceed_next : excceed_next_arr) { + task_info_ptr->exceeded_next.emplace_back(excceed_next.as_string()); + } + } + else { + task_info_ptr->exceeded_next.emplace_back("Stop"); + } + task_info_ptr->pre_delay = task_json.get("preDelay", 0); + task_info_ptr->rear_delay = task_json.get("rearDelay", 0); + if (task_json.contains("reduceOtherTimes")) { + const json::array& reduce_arr = task_json.at("reduceOtherTimes").as_array(); + for (const json::value& reduce : reduce_arr) { + task_info_ptr->reduce_other_times.emplace_back(reduce.as_string()); + } + } + if (task_json.contains("roi")) { + const json::array& area_arr = task_json.at("roi").as_array(); + int x = area_arr[0].as_integer(); + int y = area_arr[1].as_integer(); + int width = area_arr[2].as_integer(); + int height = area_arr[3].as_integer(); #ifdef ASST_DEBUG - if (x + width > WindowWidthDefault || y + height > WindowHeightDefault) { - m_last_error = name + " roi is out of bounds"; - return false; - } + if (x + width > WindowWidthDefault || y + height > WindowHeightDefault) { + m_last_error = name + " roi is out of bounds"; + return false; + } #endif - task_info_ptr->roi = Rect(x, y, width, height); - } - else { - task_info_ptr->roi = Rect(); - } + task_info_ptr->roi = Rect(x, y, width, height); + } + else { + task_info_ptr->roi = Rect(); + } - if (task_json.contains("next")) { - for (const json::value& next : task_json.at("next").as_array()) { - task_info_ptr->next.emplace_back(next.as_string()); - } - } - if (task_json.contains("rectMove")) { - const json::array& move_arr = task_json.at("rectMove").as_array(); - task_info_ptr->rect_move = Rect( - move_arr[0].as_integer(), - move_arr[1].as_integer(), - move_arr[2].as_integer(), - move_arr[3].as_integer()); - } - else { - task_info_ptr->rect_move = Rect(); - } + if (task_json.contains("next")) { + for (const json::value& next : task_json.at("next").as_array()) { + task_info_ptr->next.emplace_back(next.as_string()); + } + } + if (task_json.contains("rectMove")) { + const json::array& move_arr = task_json.at("rectMove").as_array(); + task_info_ptr->rect_move = Rect( + move_arr[0].as_integer(), + move_arr[1].as_integer(), + move_arr[2].as_integer(), + move_arr[3].as_integer()); + } + else { + task_info_ptr->rect_move = Rect(); + } - m_all_tasks_info.emplace(name, task_info_ptr); - } + m_all_tasks_info.emplace(name, task_info_ptr); + } #ifdef ASST_DEBUG - for (const auto& [name, task] : m_all_tasks_info) { - for (const auto& next : task->next) { - if (m_all_tasks_info.find(next) == m_all_tasks_info.cend()) { - m_last_error = name + "'s next " + next + " is null"; - return false; - } - } - } + for (const auto& [name, task] : m_all_tasks_info) { + for (const auto& next : task->next) { + if (m_all_tasks_info.find(next) == m_all_tasks_info.cend()) { + m_last_error = name + "'s next " + next + " is null"; + return false; + } + } + } #endif - return true; + return true; } \ No newline at end of file diff --git a/src/MeoAssistant/TilePack.cpp b/src/MeoAssistant/TilePack.cpp index 1d39497d55..e66b537249 100644 --- a/src/MeoAssistant/TilePack.cpp +++ b/src/MeoAssistant/TilePack.cpp @@ -46,7 +46,11 @@ std::unordered_map asst::TilePack::calc( { "tile_wall", TileKey::Wall }, { "tile_road", TileKey::Road }, { "tile_end", TileKey::Home }, - { "tile_start", TileKey::EnemyHome } + { "tile_start", TileKey::EnemyHome }, + { "tile_floor", TileKey::Floor }, + { "tile_hole", TileKey::Hole }, + { "tile_telin", TileKey::Telin }, + { "tile_telout", TileKey::Telout } }; for (size_t y = 0; y < pos.size(); ++y) { @@ -54,13 +58,13 @@ std::unordered_map asst::TilePack::calc( const auto& cv_p = pos[y][x]; const auto& tile = tiles[y][x]; - TileKey key = TileKey::Invaild; + TileKey key = TileKey::Invalid; if (auto iter = TileKeyMapping.find(tile.tileKey); iter != TileKeyMapping.cend()) { key = iter->second; } else { - key = TileKey::Invaild; + key = TileKey::Invalid; Log.error("Unknown tile type:", tile.tileKey); } diff --git a/src/MeoAssistant/TilePack.h b/src/MeoAssistant/TilePack.h index eb92b04ffd..1ffb46e0d4 100644 --- a/src/MeoAssistant/TilePack.h +++ b/src/MeoAssistant/TilePack.h @@ -17,7 +17,7 @@ namespace asst public: enum class BuildableType { - Invaild = -1, + Invalid = -1, None = 0, Melee = 1, Ranged = 2, @@ -25,25 +25,29 @@ namespace asst }; enum class HeightType { - Invaild = -1, + Invalid = -1, Highland = 0, Floor = 1 }; enum class TileKey { - Invaild = -1, + Invalid = -1, Forbidden, Wall, Road, Home, - EnemyHome + EnemyHome, + Floor, + Hole, + Telin, + Telout }; struct TileInfo { - BuildableType buildable = BuildableType::Invaild; - HeightType height = HeightType::Invaild; - TileKey key = TileKey::Invaild; + BuildableType buildable = BuildableType::Invalid; + HeightType height = HeightType::Invalid; + TileKey key = TileKey::Invalid; Point pos; // 像素坐标 Point loc; // 格子位置 };