From 9618968d38d23536c51f2fad05577cd76bee2e39 Mon Sep 17 00:00:00 2001 From: zzyyyl Date: Tue, 25 Oct 2022 19:10:33 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=B0=9D=E8=AF=95=E6=8A=8A=20sharp=5Ft?= =?UTF-8?q?ask=20=E7=9A=84=E7=94=9F=E6=88=90=E5=8F=98=E4=B8=BA=E9=A2=84?= =?UTF-8?q?=E5=A4=84=E7=90=86=EF=BC=8C=E5=A4=A7=E5=B9=85=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=20TaskData?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MeoAssistant/Task/Sub/ProcessTask.cpp | 64 ------ src/MeoAssistant/Task/Sub/ProcessTask.h | 2 - src/MeoAssistant/TaskData.cpp | 229 ++++++++++------------ src/MeoAssistant/TaskData.h | 95 +++++++-- 4 files changed, 179 insertions(+), 211 deletions(-) diff --git a/src/MeoAssistant/Task/Sub/ProcessTask.cpp b/src/MeoAssistant/Task/Sub/ProcessTask.cpp index 59672d087c..e98157b162 100644 --- a/src/MeoAssistant/Task/Sub/ProcessTask.cpp +++ b/src/MeoAssistant/Task/Sub/ProcessTask.cpp @@ -79,67 +79,6 @@ ProcessTask& asst::ProcessTask::set_rear_delay(std::string name, int delay) return *this; } -bool ProcessTask::generate_tasks(const std::vector& raw_tasks, std::unordered_set& tasks_set) -{ - for (const std::string& task : raw_tasks) { - if (tasks_set.contains(task)) [[unlikely]] { - // 对于相同的 task,第一次识别后第二次就不再识别了 - continue; - } - - size_t pos = task.find('#'); - if (pos == std::string::npos) [[likely]] { - m_cur_tasks_name.emplace_back(task); - tasks_set.emplace(task); - continue; - } - - std::string other_task_name = task.substr(0, pos); - std::string_view type = std::string_view(task).substr(pos + 1); - auto other_tasklist_ref = Task.get(other_task_name); - if (other_tasklist_ref == nullptr) { - Log.error(task, "not found"); - continue; - } - -#define ASST_PROCESSTASK_GENERATE_TASKS(t) \ - else if (type == #t) \ - { \ - if (!generate_tasks(other_tasklist_ref->t, tasks_set)) { \ - return false; \ - } \ - } - if constexpr (false) {} - ASST_PROCESSTASK_GENERATE_TASKS(next) - ASST_PROCESSTASK_GENERATE_TASKS(sub) - ASST_PROCESSTASK_GENERATE_TASKS(on_error_next) - ASST_PROCESSTASK_GENERATE_TASKS(exceeded_next) - ASST_PROCESSTASK_GENERATE_TASKS(reduce_other_times) - else { - Log.error("Unknown type", type); - return false; - } -#undef ASST_PROCESSTASK_GENERATE_TASKS - - tasks_set.emplace(task); - } - - return true; -} - -bool ProcessTask::generate_tasks() -{ - // std::move 后 m_cur_tasks_name 已经为空 - std::vector cur_tasks_name = std::move(m_cur_tasks_name); - std::unordered_set tasks_set {}; - if (!generate_tasks(cur_tasks_name, tasks_set)) [[unlikely]] { - Log.error("Generate task failed. pre_task:", m_pre_task_name); - m_cur_tasks_name.clear(); - return false; - } - return true; -} - bool ProcessTask::_run() { LogTraceFunction; @@ -151,9 +90,6 @@ bool ProcessTask::_run() if (m_cur_task_ptr && m_pre_task_name != m_cur_task_ptr->name) { m_pre_task_name = m_cur_task_ptr->name; } - if (!generate_tasks()) { - return false; - } json::value info = basic_info(); info["details"] = json::object { diff --git a/src/MeoAssistant/Task/Sub/ProcessTask.h b/src/MeoAssistant/Task/Sub/ProcessTask.h index 988fc7bbe1..763e06ba9f 100644 --- a/src/MeoAssistant/Task/Sub/ProcessTask.h +++ b/src/MeoAssistant/Task/Sub/ProcessTask.h @@ -41,8 +41,6 @@ namespace asst virtual json::value basic_info() const override; std::pair calc_time_limit() const; - bool generate_tasks(const std::vector& raw_tasks, std::unordered_set& tasks_set); - bool generate_tasks(); void exec_click_task(const Rect& matched_rect); void exec_swipe_task(ProcessTaskAction action); void exec_slowly_swipe_task(ProcessTaskAction action); diff --git a/src/MeoAssistant/TaskData.cpp b/src/MeoAssistant/TaskData.cpp index ae8fd02e9a..6ce6241202 100644 --- a/src/MeoAssistant/TaskData.cpp +++ b/src/MeoAssistant/TaskData.cpp @@ -18,13 +18,14 @@ const std::unordered_set& asst::TaskData::get_templ_required() cons bool asst::TaskData::parse(const json::value& json) { LogTraceFunction; + using tasklist_t = std::vector; const auto& json_obj = json.as_object(); { - std::unordered_map to_be_generated; + std::unordered_map to_be_generated; for (const std::string& name : json_obj | views::keys) { - to_be_generated[name] = true; + to_be_generated[task_name_view(name)] = true; } auto generate_task_and_its_base = [&](const std::string& name) -> bool { @@ -34,17 +35,17 @@ bool asst::TaskData::parse(const json::value& json) if (task_info_ptr == nullptr) { return false; } - to_be_generated[name] = false; - m_all_tasks_info[name] = task_info_ptr; + to_be_generated[task_name_view(name)] = false; + insert_or_assign_raw_task(name, task_info_ptr); return true; }; std::function generate_fun; // must_true 若为真,那么 return false 了就是炸了。 // 否则可能只是某个 B@A 的任务没定义 A(这不是少见现象,例如 Roguelike@Abandon) generate_fun = [&](const std::string& name, bool must_true) -> bool { - if (!to_be_generated[name]) { + if (!to_be_generated[task_name_view(name)]) { // 已生成(它是之前加载过的某个资源的 base) - if (m_all_tasks_info.contains(name)) { + if (m_raw_all_tasks_info.contains(name)) { return true; } // 不在 json 内且未生成(例如生成 C@B@A 时没有定义 B@A,而是定义了 A) @@ -60,12 +61,12 @@ bool asst::TaskData::parse(const json::value& json) const json::value& task_json = json_obj.at(name); if (auto opt = task_json.find("baseTask")) { std::string base = opt.value(); - return generate_fun(base, must_true) && generate_task(name, "", get(base, false), task_json); + return generate_fun(base, must_true) && generate_task(name, "", get_raw(base), task_json); } if (size_t p = name.find('@'); p != std::string::npos) { if (std::string base = name.substr(p + 1); generate_fun(base, false)) { - return generate_task(name, name.substr(0, p), get(base, false), task_json); + return generate_task(name, name.substr(0, p), get_raw(base), task_json); } // 这类任务有点多,非必要不输出( // Log.debug("Task", name, "based on unknown task, just use task_json."); @@ -78,6 +79,80 @@ bool asst::TaskData::parse(const json::value& json) for (const std::string& name : json_obj | views::keys) { generate_task_and_its_base(name); } + + // 生成 # 型任务 + for (const auto& [name, old_task] : m_raw_all_tasks_info) { + bool task_changed = false; + auto task_info = _generate_task_info(old_task); + auto generate_sharp_task = [&](tasklist_t& new_task_list, const tasklist_t& task_list, + std::string_view list_type) { + new_task_list.clear(); + std::function generate_tasks; + std::unordered_set tasks_set {}; + generate_tasks = [&](const tasklist_t& raw_tasks) { + for (std::string_view task : raw_tasks) { + if (tasks_set.contains(task)) [[unlikely]] { + task_changed = true; + continue; + } + + size_t pos = task.find('#'); + if (pos == std::string_view::npos) [[likely]] { + new_task_list.emplace_back(task); + tasks_set.emplace(task_name_view(task)); + continue; + } + + task_changed = true; + std::string_view type = task.substr(pos + 1); + auto other_tasklist_ref = get_raw(task.substr(0, pos)); +#define ASST_TASKDATA_GENERATE_TASKS(t) \ + else if (type == #t) \ + { \ + if (!generate_tasks(other_tasklist_ref->t)) { \ + return false; \ + } \ + } + if (other_tasklist_ref == nullptr) { + Log.error("Task", task, "not found"); + return false; + } + ASST_TASKDATA_GENERATE_TASKS(next) + ASST_TASKDATA_GENERATE_TASKS(sub) + ASST_TASKDATA_GENERATE_TASKS(on_error_next) + ASST_TASKDATA_GENERATE_TASKS(exceeded_next) + ASST_TASKDATA_GENERATE_TASKS(reduce_other_times) + else { + Log.error("Unknown type", type, "in", task); + return false; + } +#undef ASST_TASKDATA_GENERATE_TASKS + + tasks_set.emplace(task_name_view(task)); + } + + return true; + }; + if (!generate_tasks(task_list)) { + Log.error(Logger::separator::none, "Generate task_list ", name, "->", list_type, " failed."); + return false; + } + return true; + }; + +#define ASST_TASKDATA_GENERATE_SHARP_TASK(type) \ + if (!generate_sharp_task(task_info->type, old_task->type, #type)) { \ + return false; \ + } + ASST_TASKDATA_GENERATE_SHARP_TASK(next); + ASST_TASKDATA_GENERATE_SHARP_TASK(sub); + ASST_TASKDATA_GENERATE_SHARP_TASK(exceeded_next); + ASST_TASKDATA_GENERATE_SHARP_TASK(on_error_next); + ASST_TASKDATA_GENERATE_SHARP_TASK(reduce_other_times); +#undef ASST_TASKDATA_GENERATE_SHARP_TASK + + insert_or_assign_task(name, task_changed ? task_info : old_task); + } } #ifdef ASST_DEBUG @@ -89,134 +164,40 @@ bool asst::TaskData::parse(const json::value& json) validity &= syntax_check(name, task_json); } - std::unordered_map> dependency_graph; // "#" 型任务依赖关系 (有向图邻接表) - std::unordered_map checked; // 拓扑排序相关的标志 - static const std::unordered_set accepted_type = { - "next", "sub", "on_error_next", "exceeded_next", "reduce_other_times", - }; - for (const auto& [name, task] : m_all_tasks_info) { - auto check_and_link = [&](const std::vector& task_list, std::string node_name) { - for (const auto& task_name : task_list) { - size_t pos = task_name.find('#'); - // next、sub 等的存在性检查 - if (pos == std::string::npos) { - if (get(task_name, false) == nullptr) { - Log.error(node_name, task_name, "is null"); - validity = false; - } + auto check_tasklist = [&](const tasklist_t& task_list, std::string_view list_type, + bool enable_justreturn_check = false) { + std::unordered_set tasks_set {}; + std::string justreturn_task_name = ""; + for (const std::string& task_name : task_list) { + if (tasks_set.contains(task_name)) [[unlikely]] { continue; } - - std::string other_task_name = task_name.substr(0, pos); - if (get(other_task_name, false) == nullptr) { - Log.error(node_name, task_name, "is null"); + // 检查是否有 JustReturn 任务不是最后一个任务 + if (enable_justreturn_check && !justreturn_task_name.empty()) [[unlikely]] { + Log.error(Logger::separator::none, name, "->", list_type, + " has a not-final JustReturn task: ", justreturn_task_name); validity = false; } - std::string type = task_name.substr(pos + 1); - if (!accepted_type.contains(type)) { - Log.error(node_name, task_name, "has unknown type:", type); + + if (auto ptr = get_raw(task_name); ptr == nullptr) { + Log.error(Logger::separator::none, task_name, " in ", name, "->", list_type, " is null"); validity = false; } - else { - // 建立一条依赖关系 (有向边) - dependency_graph[node_name].emplace_back(task_name); + else if (ptr->algorithm == AlgorithmType::JustReturn) { + justreturn_task_name = ptr->name; } + + tasks_set.emplace(task_name_view(task_name)); } - }; - check_and_link(task->next, name + "#next"); - check_and_link(task->sub, name + "#sub"); - check_and_link(task->exceeded_next, name + "#exceeded_next"); - check_and_link(task->on_error_next, name + "#on_error_next"); - check_and_link(task->reduce_other_times, name + "#reduce_other_times"); - // 检查是否有 JustReturn 任务不是最后一个任务 - auto check_justreturn = [&](const std::vector& task_list, std::string node_name) { - std::function&)> generate_tasks; - std::unordered_set tasks_set {}; - bool has_justreturn = false; - generate_tasks = [&](const std::vector& raw_tasks) { - for (const std::string& task : raw_tasks) { - if (tasks_set.contains(task)) [[unlikely]] { - continue; - } - if (has_justreturn) [[unlikely]] { - return false; - } - - size_t pos = task.find('#'); - if (pos == std::string::npos) [[likely]] { - if (auto ptr = get(task, false); ptr != nullptr) { - has_justreturn |= ptr->algorithm == AlgorithmType::JustReturn; - } - tasks_set.emplace(task); - continue; - } - - std::string other_task_name = task.substr(0, pos); - std::string_view type = std::string_view(task).substr(pos + 1); - auto other_tasklist_ref = get(other_task_name, false); - if (other_tasklist_ref == nullptr) { - continue; - } - -#define ASST_PROCESSTASK_GENERATE_TASKS(t) \ - else if (type == #t) \ - { \ - if (!generate_tasks(other_tasklist_ref->t)) { \ - return false; \ - } \ - } - if constexpr (false) {} - ASST_PROCESSTASK_GENERATE_TASKS(next) - ASST_PROCESSTASK_GENERATE_TASKS(sub) - ASST_PROCESSTASK_GENERATE_TASKS(on_error_next) - ASST_PROCESSTASK_GENERATE_TASKS(exceeded_next) - ASST_PROCESSTASK_GENERATE_TASKS(reduce_other_times) -#undef ASST_PROCESSTASK_GENERATE_TASKS - - tasks_set.emplace(task); - } - - return true; - }; - if (!generate_tasks(task_list)) { - Log.error(node_name, "has justreturn task that is not the last one"); - validity = false; - } - }; - check_justreturn(task->next, name + "#next"); - check_justreturn(task->exceeded_next, name + "#exceeded_next"); - check_justreturn(task->on_error_next, name + "#on_error_next"); - } - - // dfs 检查 "#" 型任务是否循环依赖 (有向无环图) - auto check_circle = [&](const std::string& x) { - std::function dfs; - dfs = [&](const std::string& x) { - checked[x] = -1; - for (const auto& y : dependency_graph[x]) { - if (checked[y] == 0) { - if (!dfs(y)) [[unlikely]] { - return false; - } - } - else if (checked[y] < 0) [[unlikely]] { - Log.error("Task", y, "has circular dependency."); - return false; - } - } - checked[x] = 1; return true; }; - return dfs(x); - }; - - for (const auto& name : dependency_graph | views::keys) { - if (!checked[name] && !check_circle(name)) { - validity = false; - break; - } + check_tasklist(task->next, "next", true); + check_tasklist(task->sub, "sub"); + check_tasklist(task->exceeded_next, "exceeded_next", true); + check_tasklist(task->on_error_next, "on_error_next", true); + check_tasklist(task->reduce_other_times, "reduce_other_times"); } if (!validity) return false; diff --git a/src/MeoAssistant/TaskData.h b/src/MeoAssistant/TaskData.h index 3a0cdef35b..6d94a98d39 100644 --- a/src/MeoAssistant/TaskData.h +++ b/src/MeoAssistant/TaskData.h @@ -76,7 +76,7 @@ namespace asst }; // 运行时动态生成任务 static std::shared_ptr _generate_task_info(const std::shared_ptr& base_ptr, - const std::string& task_prefix) + std::string_view task_prefix = "") { std::shared_ptr task_info_ptr; switch (base_ptr->algorithm) { @@ -94,16 +94,28 @@ namespace asst break; } - task_info_ptr->name = task_prefix + "@" + base_ptr->name; - task_info_ptr->sub = append_prefix(base_ptr->sub, task_prefix); - task_info_ptr->next = append_prefix(base_ptr->next, task_prefix); - task_info_ptr->exceeded_next = append_prefix(base_ptr->exceeded_next, task_prefix); - task_info_ptr->on_error_next = append_prefix(base_ptr->on_error_next, task_prefix); - task_info_ptr->reduce_other_times = append_prefix(base_ptr->reduce_other_times, task_prefix); + if (!task_prefix.empty()) { + task_info_ptr->name = std::string(task_prefix) + "@" + base_ptr->name; + task_info_ptr->sub = append_prefix(base_ptr->sub, task_prefix); + task_info_ptr->next = append_prefix(base_ptr->next, task_prefix); + task_info_ptr->exceeded_next = append_prefix(base_ptr->exceeded_next, task_prefix); + task_info_ptr->on_error_next = append_prefix(base_ptr->on_error_next, task_prefix); + task_info_ptr->reduce_other_times = append_prefix(base_ptr->reduce_other_times, task_prefix); + } return task_info_ptr; } + std::string_view task_name_view(std::string_view task_name) { return *m_task_names.emplace(task_name).first; } + void insert_or_assign_raw_task(std::string_view task_name, std::shared_ptr task_info_ptr) + { + m_raw_all_tasks_info.insert_or_assign(task_name_view(task_name), task_info_ptr); + } + void insert_or_assign_task(std::string_view task_name, std::shared_ptr task_info_ptr) + { + m_all_tasks_info.insert_or_assign(task_name_view(task_name), task_info_ptr); + } + public: virtual ~TaskData() override = default; virtual const std::unordered_set& get_templ_required() const noexcept override; @@ -111,7 +123,7 @@ namespace asst template requires(std::derived_from || std::same_as) // Parameter must be a TaskInfo - std::shared_ptr get(const std::string& name, bool with_emplace = true) + std::shared_ptr get(std::string_view name) { // 普通 task 或已经生成过的 `@` 型 task if (auto it = m_all_tasks_info.find(name); it != m_all_tasks_info.cend()) [[likely]] { @@ -124,18 +136,18 @@ namespace asst } size_t at_pos = name.find('@'); - if (at_pos == std::string::npos) [[unlikely]] { + if (at_pos == std::string_view::npos) [[unlikely]] { return nullptr; } // `@` 前面的字符长度 size_t name_len = at_pos; - auto base_task_iter = get(name.substr(name_len + 1), with_emplace); + auto base_task_iter = get(name.substr(name_len + 1)); if (base_task_iter == nullptr) [[unlikely]] { return nullptr; } - std::string derived_task_name = name.substr(0, name_len); + std::string_view derived_task_name = name.substr(0, name_len); auto task_info_ptr = _generate_task_info(base_task_iter, derived_task_name); if (task_info_ptr == nullptr) [[unlikely]] { return nullptr; @@ -143,16 +155,14 @@ namespace asst // tasks 个数超过上限时不再 emplace,返回临时值 constexpr size_t MAX_TASKS_SIZE = 65535; - if (with_emplace) { - if (m_all_tasks_info.size() < MAX_TASKS_SIZE) { - m_all_tasks_info.emplace(name, task_info_ptr); - } -#ifdef ASST_DEBUG - else { - Log.debug("Task count has exceeded the upper limit:", MAX_TASKS_SIZE, "current task:", name); - } -#endif // ASST_DEBUG + if (m_all_tasks_info.size() < MAX_TASKS_SIZE) [[likely]] { + insert_or_assign_task(name, task_info_ptr); } +#ifdef ASST_DEBUG + else { + Log.debug("Task count has exceeded the upper limit:", MAX_TASKS_SIZE, "current task:", name); + } +#endif // ASST_DEBUG if constexpr (std::same_as) { return task_info_ptr; @@ -165,13 +175,56 @@ namespace asst protected: virtual bool parse(const json::value& json) override; + template + requires(std::derived_from || + std::same_as) // Parameter must be a TaskInfo + std::shared_ptr get_raw(std::string_view name) const + { + // 普通 task 或已经生成过的 `@` 型 task + if (auto it = m_raw_all_tasks_info.find(name); it != m_raw_all_tasks_info.cend()) [[likely]] { + if constexpr (std::same_as) { + return it->second; + } + else { + return std::dynamic_pointer_cast(it->second); + } + } + + size_t at_pos = name.find('@'); + if (at_pos == std::string_view::npos) [[unlikely]] { + return nullptr; + } + + // `@` 前面的字符长度 + size_t name_len = at_pos; + auto base_task_iter = get_raw(name.substr(name_len + 1)); + if (base_task_iter == nullptr) [[unlikely]] { + return nullptr; + } + + std::string_view derived_task_name = name.substr(0, name_len); + auto task_info_ptr = _generate_task_info(base_task_iter, derived_task_name); + if (task_info_ptr == nullptr) [[unlikely]] { + return nullptr; + } + + if constexpr (std::same_as) { + return task_info_ptr; + } + else { + return std::dynamic_pointer_cast(task_info_ptr); + } + } + private: #ifdef ASST_DEBUG bool syntax_check(const std::string& task_name, const json::value& task_json); #endif protected: - std::unordered_map> m_all_tasks_info; + std::unordered_set m_task_names; + std::unordered_map> m_raw_all_tasks_info; + std::unordered_map> m_all_tasks_info; std::unordered_set m_templ_required; };