feat: 尝试把 sharp_task 的生成变为预处理,大幅修改 TaskData

This commit is contained in:
zzyyyl
2022-10-25 19:10:33 +08:00
parent a541c3e525
commit 9618968d38
4 changed files with 179 additions and 211 deletions

View File

@@ -79,67 +79,6 @@ ProcessTask& asst::ProcessTask::set_rear_delay(std::string name, int delay)
return *this;
}
bool ProcessTask::generate_tasks(const std::vector<std::string>& raw_tasks, std::unordered_set<std::string>& 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<std::string> cur_tasks_name = std::move(m_cur_tasks_name);
std::unordered_set<std::string> 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 {

View File

@@ -41,8 +41,6 @@ namespace asst
virtual json::value basic_info() const override;
std::pair<int, TimesLimitType> calc_time_limit() const;
bool generate_tasks(const std::vector<std::string>& raw_tasks, std::unordered_set<std::string>& 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);

View File

@@ -18,13 +18,14 @@ const std::unordered_set<std::string>& asst::TaskData::get_templ_required() cons
bool asst::TaskData::parse(const json::value& json)
{
LogTraceFunction;
using tasklist_t = std::vector<std::string>;
const auto& json_obj = json.as_object();
{
std::unordered_map<std::string, bool> to_be_generated;
std::unordered_map<std::string_view, bool> 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<bool(const std::string&, bool)> 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<std::string>("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<bool(const tasklist_t&)> generate_tasks;
std::unordered_set<std::string_view> 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<std::string, std::vector<std::string>> dependency_graph; // "#" 型任务依赖关系 (有向图邻接表)
std::unordered_map<std::string, int> checked; // 拓扑排序相关的标志
static const std::unordered_set<std::string> 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<std::string>& 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<std::string_view> 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<std::string>& task_list, std::string node_name) {
std::function<bool(const std::vector<std::string>&)> generate_tasks;
std::unordered_set<std::string> tasks_set {};
bool has_justreturn = false;
generate_tasks = [&](const std::vector<std::string>& 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<bool(const std::string&)> 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;

View File

@@ -76,7 +76,7 @@ namespace asst
};
// 运行时动态生成任务
static std::shared_ptr<TaskInfo> _generate_task_info(const std::shared_ptr<TaskInfo>& base_ptr,
const std::string& task_prefix)
std::string_view task_prefix = "")
{
std::shared_ptr<TaskInfo> 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<TaskInfo> 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<TaskInfo> 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<std::string>& get_templ_required() const noexcept override;
@@ -111,7 +123,7 @@ namespace asst
template <typename TargetTaskInfoType = TaskInfo>
requires(std::derived_from<TargetTaskInfoType, TaskInfo> ||
std::same_as<TargetTaskInfoType, TaskInfo>) // Parameter must be a TaskInfo
std::shared_ptr<TargetTaskInfoType> get(const std::string& name, bool with_emplace = true)
std::shared_ptr<TargetTaskInfoType> 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<TargetTaskInfoType, TaskInfo>) {
return task_info_ptr;
@@ -165,13 +175,56 @@ namespace asst
protected:
virtual bool parse(const json::value& json) override;
template <typename TargetTaskInfoType = TaskInfo>
requires(std::derived_from<TargetTaskInfoType, TaskInfo> ||
std::same_as<TargetTaskInfoType, TaskInfo>) // Parameter must be a TaskInfo
std::shared_ptr<TargetTaskInfoType> 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<TargetTaskInfoType, TaskInfo>) {
return it->second;
}
else {
return std::dynamic_pointer_cast<TargetTaskInfoType>(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<TargetTaskInfoType, TaskInfo>) {
return task_info_ptr;
}
else {
return std::dynamic_pointer_cast<TargetTaskInfoType>(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<std::string, std::shared_ptr<TaskInfo>> m_all_tasks_info;
std::unordered_set<std::string> m_task_names;
std::unordered_map<std::string_view, std::shared_ptr<TaskInfo>> m_raw_all_tasks_info;
std::unordered_map<std::string_view, std::shared_ptr<TaskInfo>> m_all_tasks_info;
std::unordered_set<std::string> m_templ_required;
};