diff --git a/MeoAssistance/AbstractTask.cpp b/MeoAssistance/AbstractTask.cpp index 11ecf772c3..fe7c508edd 100644 --- a/MeoAssistance/AbstractTask.cpp +++ b/MeoAssistance/AbstractTask.cpp @@ -55,20 +55,20 @@ cv::Mat AbstractTask::get_format_image(bool raw) return raw_image; } else { - constexpr double DefaultRatio = static_cast(Configer::DefaultWindowWidth) / static_cast(Configer::DefaultWindowHeight); + constexpr double DefaultRatio = static_cast(Configer::WindowWidthDefault) / static_cast(Configer::WindowHeightDefault); double cur_ratio = static_cast(raw_image.cols) / static_cast(raw_image.rows); cv::Size scale_size; double scale = 0.0; if (cur_ratio >= DefaultRatio // 说明是宽屏或默认16:9,按照高度计算缩放 || std::fabs(cur_ratio - DefaultRatio) < DoubleDiff) { - scale_size = cv::Size(cur_ratio * Configer::DefaultWindowHeight, Configer::DefaultWindowHeight); - scale = static_cast(raw_image.rows) / static_cast(Configer::DefaultWindowHeight); + scale_size = cv::Size(cur_ratio * Configer::WindowHeightDefault, Configer::WindowHeightDefault); + scale = static_cast(raw_image.rows) / static_cast(Configer::WindowHeightDefault); } else { // 否则可能是偏正方形的屏幕,按宽度计算 - scale_size = cv::Size(Configer::DefaultWindowWidth, Configer::DefaultWindowWidth / cur_ratio); - scale = static_cast(raw_image.cols) / static_cast(Configer::DefaultWindowWidth); + scale_size = cv::Size(Configer::WindowWidthDefault, Configer::WindowWidthDefault / cur_ratio); + scale = static_cast(raw_image.cols) / static_cast(Configer::WindowWidthDefault); } cv::Mat resize_mat; cv::resize(raw_image, resize_mat, scale_size); @@ -86,8 +86,8 @@ bool asst::AbstractTask::set_control_scale(double scale) //bool AbstractTask::set_control_scale(int cur_width, int cur_height) //{ -// double scale_width = static_cast(cur_width) / Configer::DefaultWindowWidth; -// double scale_height = static_cast(cur_height) / Configer::DefaultWindowHeight; +// double scale_width = static_cast(cur_width) / Configer::WindowWidthDefault; +// double scale_height = static_cast(cur_height) / Configer::WindowHeightDefault; // double scale = (std::max)(scale_width, scale_height); // m_control_ptr->setControlScale(scale); // return true; diff --git a/MeoAssistance/Assistance.cpp b/MeoAssistance/Assistance.cpp index 66415819b5..c73b8f0114 100644 --- a/MeoAssistance/Assistance.cpp +++ b/MeoAssistance/Assistance.cpp @@ -60,6 +60,14 @@ Assistance::Assistance(AsstCallback callback, void* callback_arg) return; } } + for (auto& p : std::filesystem::directory_iterator(GetResourceDir() + "template\\special\\")) { + if (p.path().extension() == ".png") { + std::string filename = p.path().filename().u8string(); + std::string without_extension = filename.substr(0, filename.size() - 4); + ret = m_identify_ptr->add_image(without_extension, p.path().u8string()); + } + } + m_identify_ptr->set_use_cache(Configer::get_instance().m_options.identify_cache); m_identify_ptr->set_ocr_param(Configer::get_instance().m_options.ocr_gpu_index, Configer::get_instance().m_options.ocr_thread_number); @@ -84,14 +92,6 @@ Assistance::Assistance(AsstCallback callback, void* callback_arg) } } - // 精一和精二的图片,调试用 - ret = m_identify_ptr->add_text_image("Elite1", GetResourceDir() + "operators\\Elite1.png"); - ret &= m_identify_ptr->add_text_image("Elite2", GetResourceDir() + "operators\\Elite2.png"); - if (!ret) { - callback_error(); - return; - } - m_working_thread = std::thread(working_proc, this); m_msg_thread = std::thread(msg_proc, this); } @@ -296,11 +296,10 @@ bool asst::Assistance::start_infrast() // TODO,这里需要根据用户设置,是先制造站还是先贸易站,或者是别的设施 // 从进入制造站,到进入干员选择界面清空选择 - append_match_task(InfrastTaskCahin, { "Manufacturing", "ManufacturingMini" }); + append_match_task(InfrastTaskCahin, { "ManufacturingMini", "Manufacturing" }); // 识别并选择最优解干员组合 auto task_ptr = std::make_shared(task_callback, (void*)this); - task_ptr->set_facility("Manufacturing"); task_ptr->set_task_chain(InfrastTaskCahin); task_ptr->set_all_opers_info(std::move(ret.value())); m_tasks_deque.emplace_back(task_ptr); diff --git a/MeoAssistance/Configer.cpp b/MeoAssistance/Configer.cpp index e22dea28d7..70d739b006 100644 --- a/MeoAssistance/Configer.cpp +++ b/MeoAssistance/Configer.cpp @@ -98,8 +98,8 @@ bool asst::Configer::parse(json::value&& json) { auto match_task_info_ptr = std::make_shared(); match_task_info_ptr->template_filename = task_json["template"].as_string(); - match_task_info_ptr->templ_threshold = task_json.get("templThreshold", Defaulttempl_threshold); - match_task_info_ptr->hist_threshold = task_json.get("histThreshold", DefaultCachetempl_threshold); + match_task_info_ptr->templ_threshold = task_json.get("templThreshold", TemplThresholdDefault); + match_task_info_ptr->hist_threshold = task_json.get("histThreshold", HistThresholdDefault); match_task_info_ptr->cache = task_json.get("cache", true); task_info_ptr = match_task_info_ptr; } @@ -220,8 +220,8 @@ bool asst::Configer::parse(json::value&& json) emulator_info.right_offset = emulator_json.get("rightOffset", 0); emulator_info.bottom_offset = emulator_json.get("bottomOffset", 0); - emulator_info.width = DefaultWindowWidth + emulator_info.x_offset + emulator_info.right_offset; - emulator_info.height = DefaultWindowHeight + emulator_info.y_offset + emulator_info.bottom_offset; + emulator_info.width = WindowWidthDefault + emulator_info.x_offset + emulator_info.right_offset; + emulator_info.height = WindowHeightDefault + emulator_info.y_offset + emulator_info.bottom_offset; m_handles.emplace(name, std::move(emulator_info)); } diff --git a/MeoAssistance/Configer.h b/MeoAssistance/Configer.h index 48eb5d1d60..44cca0bca0 100644 --- a/MeoAssistance/Configer.h +++ b/MeoAssistance/Configer.h @@ -23,10 +23,10 @@ namespace asst { bool set_param(const std::string& type, const std::string& param, const std::string& value); - constexpr static int DefaultWindowWidth = 1280; - constexpr static int DefaultWindowHeight = 720; - constexpr static double Defaulttempl_threshold = 0.9; - constexpr static double DefaultCachetempl_threshold = 0.9; + constexpr static int WindowWidthDefault = 1280; + constexpr static int WindowHeightDefault = 720; + constexpr static double TemplThresholdDefault = 0.9; + constexpr static double HistThresholdDefault = 0.9; std::string m_version; Options m_options; diff --git a/MeoAssistance/Identify.cpp b/MeoAssistance/Identify.cpp index 8766242a9e..3f473a4720 100644 --- a/MeoAssistance/Identify.cpp +++ b/MeoAssistance/Identify.cpp @@ -278,29 +278,33 @@ std::pair Identify::match_template(const cv::Mat& image, cons return { maxVal, maxLoc }; } -std::tuple Identify::find_image( - const Mat& cur, const std::string& templ, double templ_threshold, bool use_cache) +std::pair asst::Identify::find_image( + const cv::Mat& image, const std::string& templ, asst::Rect* out_rect, double add_cache_thres) { if (m_mat_map.find(templ) == m_mat_map.cend()) { - return { AlgorithmType::JustReturn, 0, asst::Rect() }; + return { AlgorithmType::JustReturn, 0 }; } // 有缓存,用直方图比较,CPU占用会低很多,但要保证每次按钮图片的位置不变 - if (use_cache && m_use_cache && m_cache_map.find(templ) != m_cache_map.cend()) { - const auto& [rect, hist] = m_cache_map.at(templ); - double value = image_hist_comp(cur(rect), hist); - return { AlgorithmType::CompareHist, value, cvrect_2_rect(rect).center_zoom(0.8) }; + if (m_use_cache && m_cache_map.find(templ) != m_cache_map.cend()) { + const auto& [raw_rect, hist] = m_cache_map.at(templ); + double value = image_hist_comp(image(raw_rect), hist); + if (out_rect) { + *out_rect = cvrect_2_rect(raw_rect).center_zoom(0.8); + } + return { AlgorithmType::CompareHist, value }; } else { // 没缓存就模板匹配 const cv::Mat& templ_mat = m_mat_map.at(templ); - const auto& [value, point] = match_template(cur, templ_mat); + const auto& [value, point] = match_template(image, templ_mat); cv::Rect raw_rect(point.x, point.y, templ_mat.cols, templ_mat.rows); - - if (use_cache && m_use_cache && value >= templ_threshold) { - m_cache_map.emplace(templ, std::make_pair(raw_rect, image_2_hist(cur(raw_rect)))); + if (m_use_cache && value >= add_cache_thres) { + m_cache_map.emplace(templ, std::make_pair(raw_rect, image_2_hist(image(raw_rect)))); + } + if (out_rect) { + *out_rect = cvrect_2_rect(raw_rect).center_zoom(0.8); } - - return { AlgorithmType::MatchTemplate, value, cvrect_2_rect(raw_rect).center_zoom(0.8) }; + return { AlgorithmType::MatchTemplate, value }; } } diff --git a/MeoAssistance/Identify.h b/MeoAssistance/Identify.h index 90f40879ec..27958d8d4c 100644 --- a/MeoAssistance/Identify.h +++ b/MeoAssistance/Identify.h @@ -25,9 +25,10 @@ namespace asst { bool add_image(const std::string& name, const std::string& path); bool add_text_image(const std::string& text, const std::string& path); - // return tuple< algorithmType, suitability, matched asst::rect> - // 参数use_cache,是否使用缓存,仅针对当前图片的单独开关 - std::tuple find_image(const cv::Mat& image, const std::string& templ, double templ_threshold, bool add_cache = true); + constexpr static double NotAddCache = 999.0; + // return pair< algorithmType, suitability> + std::pair find_image( + const cv::Mat& image, const std::string& templ, asst::Rect* out_rect, double add_cache_thres = NotAddCache); // return pair< suitability, raw opencv::point> std::pair match_template(const cv::Mat& cur, const cv::Mat& templ); diff --git a/MeoAssistance/IdentifyOperTask.cpp b/MeoAssistance/IdentifyOperTask.cpp index 897e0bce2b..dc26bb30fe 100644 --- a/MeoAssistance/IdentifyOperTask.cpp +++ b/MeoAssistance/IdentifyOperTask.cpp @@ -366,12 +366,14 @@ bool IdentifyOperTask::swipe(bool reverse) //#ifndef LOG_TRACE bool ret = true; if (!reverse) { - ret &= m_control_ptr->swipe(m_swipe_begin, m_swipe_end, SwipeDuration); + ret &= m_control_ptr->swipe(m_swipe_begin, m_swipe_end, m_swipe_duration); + ++m_swipe_times; } else { - ret &= m_control_ptr->swipe(m_swipe_end, m_swipe_begin, SwipeDuration); + ret &= m_control_ptr->swipe(m_swipe_end, m_swipe_begin, m_swipe_duration); + --m_swipe_times; } - ret &= sleep(SwipeExtraDelay); + ret &= sleep(m_swipe_extra_delay); return ret; //#else // return sleep(SwipeExtraDelay); diff --git a/MeoAssistance/IdentifyOperTask.h b/MeoAssistance/IdentifyOperTask.h index 325c82f844..41e6731cfa 100644 --- a/MeoAssistance/IdentifyOperTask.h +++ b/MeoAssistance/IdentifyOperTask.h @@ -13,8 +13,8 @@ namespace asst { virtual bool run() override; protected: - constexpr static int SwipeDuration = 2000; - constexpr static int SwipeExtraDelay = 1000; + constexpr static int SwipeExtraDelayDefault = 1000; + constexpr static int SwipeDurationDefault = 2000; // 一边滑动一边识别 virtual std::optional> swipe_and_detect(); @@ -32,8 +32,11 @@ namespace asst { double m_cropped_height_ratio = 0; // 图片裁剪出干员名的长条形图片 的高度比例(相比原图) double m_cropped_upper_y_ratio = 0; // 图片裁剪出干员名的长条形图片,上半部分干员名的裁剪区域y坐标比例(相比原图) double m_cropped_lower_y_ratio = 0; // 图片裁剪出干员名的长条形图片,下半部分干员名的裁剪区域y坐标比例(相比原图) - Rect m_swipe_begin; // 边滑动边识别,单次滑动起始点(Rect内随机点) - Rect m_swipe_end; // 边滑动边识别,单次滑动结束点(Rect内随机点) - bool m_keep_swipe = false; // keep_swipe函数是否结束的标志位 + Rect m_swipe_begin; // 边滑动边识别,单次滑动起始点(Rect内随机点) + Rect m_swipe_end; // 边滑动边识别,单次滑动结束点(Rect内随机点) + int m_swipe_duration = SwipeDurationDefault; // 滑动持续时间,时间越长滑的越慢 + int m_swipe_extra_delay = SwipeExtraDelayDefault; // 滑动之后额外的等待时间 + int m_swipe_times = 0; // 滑动了几次,正向滑动增加,反向滑动减少 + bool m_keep_swipe = false; // keep_swipe函数是否结束的标志位 }; } diff --git a/MeoAssistance/InfrastStationTask.cpp b/MeoAssistance/InfrastStationTask.cpp index 7918d28906..6dd04de77b 100644 --- a/MeoAssistance/InfrastStationTask.cpp +++ b/MeoAssistance/InfrastStationTask.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -32,23 +33,73 @@ bool asst::InfrastStationTask::run() m_callback(AsstMsg::PtrIsNull, json::value(), m_callback_arg); return false; } - auto&& [width, height] = m_view_ptr->getAdbDisplaySize(); - m_swipe_begin = Rect(width * 0.9, height * 0.5, 0, 0); - m_swipe_end = Rect(width * 0.5, height * 0.5, 0, 0); - auto detect_ret = swipe_and_detect(); - if (!detect_ret) { - return false; + cv::Mat image = get_format_image(); + // 先识别一下有几个制造站/贸易站 + const static std::vector facility_number_key = { "02", "03", "04", "05" }; + std::vector facility_number_rect; + facility_number_rect.emplace_back(Rect()); // 假装给01 push一个,后面循环好写=。= + + for (const std::string& key : facility_number_key) { + Rect temp_rect; + auto&& [algorithm, value] = m_identify_ptr->find_image(image, key, &temp_rect); + if (value >= Configer::TemplThresholdDefault) { + facility_number_rect.emplace_back(temp_rect); + } + else { + break; + } } - auto cur_opers_info = std::move(detect_ret.value()); - std::vector optimal_comb = calc_optimal_comb(cur_opers_info); - bool select_ret = swipe_and_select(optimal_comb); + for (const Rect& rect : facility_number_rect) { + // 点到这个基建 + m_control_ptr->click(rect); + sleep(300); - return select_ret; + cv::Mat image = get_format_image(); + // 如果当前界面没有添加干员的按钮,那就不换班 + Rect add_rect; + auto&& [algorithm, value] = m_identify_ptr->find_image(image, "AddOperator", &add_rect); + if (value < Configer::TemplThresholdDefault) { + continue; + } + + // 识别当前正在造什么 + for (const auto& [key, useless_value] : InfrastConfiger::get_instance().m_infrast_combs) { + auto&& [algorithm, value] = m_identify_ptr->find_image(image, key, nullptr); + if (value >= Configer::TemplThresholdDefault) { + m_facility = key; + break; + } + } + //点击添加干员的那个按钮 + m_control_ptr->click(add_rect); + sleep(2000); + + // 点击“清空选择”按钮 + m_control_ptr->click(Rect(430, 655, 150, 40)); + sleep(300); + + auto&& [width, height] = m_view_ptr->getAdbDisplaySize(); + m_swipe_begin = Rect(width * 0.9, height * 0.5, 0, 0); + m_swipe_end = Rect(width * 0.5, height * 0.5, 0, 0); + + auto detect_ret = swipe_and_detect(); + if (!detect_ret) { + return false; + } + auto cur_opers_info = std::move(detect_ret.value()); + std::list optimal_comb = calc_optimal_comb(cur_opers_info); + bool select_ret = swipe_and_select(optimal_comb); + } + + return true; } std::optional> asst::InfrastStationTask::swipe_and_detect() { + if (!swipe_to_the_left()) { + return std::nullopt; + } std::unordered_map feature_cond = InfrastConfiger::get_instance().m_oper_name_feat; std::unordered_set feature_whatever = InfrastConfiger::get_instance().m_oper_name_feat_whatever; @@ -113,16 +164,16 @@ std::optional> asst::InfrastSta return cur_opers_info; } -std::vector asst::InfrastStationTask::calc_optimal_comb( +std::list asst::InfrastStationTask::calc_optimal_comb( const std::unordered_map& cur_opers_info) const { // 配置文件中的干员组合,和抓出来的干员名比对,如果组合中的干员都有,那就用这个组合 // 注意配置中的干员组合需要是有序的 // Todo 时间复杂度起飞了,需要优化下 - std::vector optimal_comb; // OperInfrastInfo是带精英化和等级信息的,基建里识别不到,也用不到,这里只保留干员名 + std::list optimal_comb; // OperInfrastInfo是带精英化和等级信息的,基建里识别不到,也用不到,这里只保留干员名 for (const auto& name_vec : InfrastConfiger::get_instance().m_infrast_combs[m_facility]) { int count = 0; - std::vector temp_comb; + std::list temp_comb; for (const OperInfrastInfo& info : name_vec) { // 找到了干员名,而且当前精英化等级需要大于等于配置文件中要求的精英化等级 if (cur_opers_info.find(info.name) != cur_opers_info.cend() @@ -150,8 +201,12 @@ std::vector asst::InfrastStationTask::calc_optimal_comb( return optimal_comb; } -bool asst::InfrastStationTask::swipe_and_select(std::vector& name_comb, int swipe_max_times) +bool asst::InfrastStationTask::swipe_and_select(std::list& name_comb, int swipe_max_times) { + //if (!swipe_to_the_left()) { + // return false; + //} + std::unordered_map feature_cond = InfrastConfiger::get_instance().m_oper_name_feat; std::unordered_set feature_whatever = InfrastConfiger::get_instance().m_oper_name_feat_whatever; // 一边滑动一边点击最优解中的干员 @@ -164,6 +219,7 @@ bool asst::InfrastStationTask::swipe_and_select(std::vector& name_c auto iter = std::find(name_comb.begin(), name_comb.end(), text_area.text); if (iter != name_comb.end()) { m_control_ptr->click(text_area.rect); + sleep(200); name_comb.erase(iter); } } @@ -175,5 +231,28 @@ bool asst::InfrastStationTask::swipe_and_select(std::vector& name_c return false; } } + // 点击“确定”按钮,确定完要联网加载的,比较慢,多sleep一会 + get_format_image(); // 这里get image没什么用,单纯就是为了触发下设置缩放,TODO 优化下 + m_control_ptr->click(Rect(1105, 655, 150, 40)); + sleep(2000); return true; } + +bool asst::InfrastStationTask::swipe_to_the_left() +{ + set_control_scale(1.0); + m_swipe_duration = 100; + m_swipe_extra_delay = 0; + // 往左使劲滑几下 + bool ret = false; + for (int i = 0; i != 5; ++i) { + ret = swipe(true); + if (!ret) { + break; + } + } + m_swipe_duration = SwipeDurationDefault; + m_swipe_extra_delay = SwipeExtraDelayDefault; + sleep(SwipeExtraDelayDefault); + return ret; +} diff --git a/MeoAssistance/InfrastStationTask.h b/MeoAssistance/InfrastStationTask.h index bb53816ed7..4e8fcbf8f6 100644 --- a/MeoAssistance/InfrastStationTask.h +++ b/MeoAssistance/InfrastStationTask.h @@ -12,9 +12,6 @@ namespace asst { virtual bool run() override; - void set_facility(std::string facility) { - m_facility = std::move(facility); - } void set_all_opers_info(std::unordered_map infos) { m_all_opers_info = std::move(infos); } @@ -24,9 +21,11 @@ namespace asst { // 一边滑动一边识别 virtual std::optional> swipe_and_detect() override; // 计算最优解干员组合 - std::vector calc_optimal_comb(const std::unordered_map& cur_opers_info) const; + std::list calc_optimal_comb(const std::unordered_map& cur_opers_info) const; // 一边滑动一边识别并点击干员名 - bool swipe_and_select(std::vector& name_comb, int swipe_max_times = SwipeMaxTimes); + bool swipe_and_select(std::list& name_comb, int swipe_max_times = SwipeMaxTimes); + // 快速滑动到最左边 + bool swipe_to_the_left(); std::string m_facility; // 设施名(制造站、贸易站、控制中枢……) std::unordered_map m_all_opers_info; diff --git a/MeoAssistance/OcrAbstractTask.cpp b/MeoAssistance/OcrAbstractTask.cpp index 3965df88ee..8b6abf7703 100644 --- a/MeoAssistance/OcrAbstractTask.cpp +++ b/MeoAssistance/OcrAbstractTask.cpp @@ -4,6 +4,7 @@ #include "AsstAux.h" #include "Identify.h" +#include "WinMacro.h" using namespace asst; @@ -34,4 +35,15 @@ std::vector