mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-19 10:32:19 +08:00
优化基建进驻,会先识别当前可用干员
This commit is contained in:
@@ -529,7 +529,7 @@ void asst::Assistance::set_opers_idtf_result(const json::value& detail)
|
||||
ofs.close();
|
||||
}
|
||||
|
||||
std::optional<std::unordered_set<OperInfrastInfo>> asst::Assistance::get_opers_idtf_result()
|
||||
std::optional<std::unordered_map<std::string, OperInfrastInfo>> Assistance::get_opers_idtf_result()
|
||||
{
|
||||
constexpr static const char* Filename = "operators.json";
|
||||
std::ifstream ifs(GetCurrentDir() + Filename, std::ios::in);
|
||||
@@ -547,7 +547,7 @@ std::optional<std::unordered_set<OperInfrastInfo>> asst::Assistance::get_opers_i
|
||||
return std::nullopt;
|
||||
}
|
||||
json::value root = std::move(parse_ret.value());
|
||||
std::unordered_set<OperInfrastInfo> opers_info;
|
||||
std::unordered_map<std::string, OperInfrastInfo> opers_info;
|
||||
try {
|
||||
for (const json::value& info_json : root["all"].as_array())
|
||||
{
|
||||
@@ -555,7 +555,7 @@ std::optional<std::unordered_set<OperInfrastInfo>> asst::Assistance::get_opers_i
|
||||
info.name = info_json.at("name").as_string();
|
||||
info.elite = info_json.at("elite").as_integer();
|
||||
info.level = info_json.get("level", 0);
|
||||
opers_info.emplace(std::move(info));
|
||||
opers_info.emplace(info.name, std::move(info));
|
||||
}
|
||||
}
|
||||
catch (json::exception& exp) {
|
||||
|
||||
@@ -64,7 +64,8 @@ namespace asst {
|
||||
void append_callback(AsstMsg msg, json::value detail);
|
||||
void clear_exec_times();
|
||||
static void set_opers_idtf_result(const json::value& detail); // 保存干员识别结果
|
||||
static std::optional<std::unordered_set<OperInfrastInfo>> get_opers_idtf_result(); // 读取干员识别结果
|
||||
static std::optional<std::unordered_map<std::string, OperInfrastInfo>>
|
||||
get_opers_idtf_result(); // 读取干员识别结果
|
||||
|
||||
std::shared_ptr<WinMacro> m_window_ptr = nullptr;
|
||||
std::shared_ptr<WinMacro> m_view_ptr = nullptr;
|
||||
|
||||
@@ -50,7 +50,7 @@ bool asst::IdentifyOperTask::run()
|
||||
|
||||
json::value result_json;
|
||||
std::vector<json::value> opers_json_vec;
|
||||
for (const OperInfrastInfo& info : detected_opers) {
|
||||
for (const auto& [name, info] : detected_opers) {
|
||||
json::value info_json;
|
||||
info_json["name"] = info.name;
|
||||
info_json["elite"] = info.elite;
|
||||
@@ -63,8 +63,8 @@ bool asst::IdentifyOperTask::run()
|
||||
std::vector<json::value> not_found_vector;
|
||||
for (const std::string& name : InfrastConfiger::get_instance().m_all_opers_name) {
|
||||
auto iter = std::find_if(detected_opers.cbegin(), detected_opers.cend(),
|
||||
[&](const OperInfrastInfo& oper) -> bool {
|
||||
return oper.name == name;
|
||||
[&](const auto& pair) -> bool {
|
||||
return pair.first == name;
|
||||
});
|
||||
if (iter == detected_opers.cend()) {
|
||||
not_found_vector.emplace_back(name);
|
||||
@@ -78,7 +78,7 @@ bool asst::IdentifyOperTask::run()
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<std::unordered_set<OperInfrastInfo>> asst::IdentifyOperTask::swipe_and_detect()
|
||||
std::optional<std::unordered_map<std::string, OperInfrastInfo>> asst::IdentifyOperTask::swipe_and_detect()
|
||||
{
|
||||
json::value task_start_json = json::object{
|
||||
{ "task_type", "InfrastStationTask" },
|
||||
@@ -88,7 +88,7 @@ std::optional<std::unordered_set<OperInfrastInfo>> asst::IdentifyOperTask::swipe
|
||||
|
||||
std::unordered_map<std::string, std::string> feature_cond = InfrastConfiger::get_instance().m_oper_name_feat;
|
||||
std::unordered_set<std::string> feature_whatever = InfrastConfiger::get_instance().m_oper_name_feat_whatever;
|
||||
std::unordered_set<OperInfrastInfo> detected_opers;
|
||||
std::unordered_map<std::string, OperInfrastInfo> detected_opers;
|
||||
|
||||
int times = 0;
|
||||
bool reverse = false;
|
||||
@@ -113,12 +113,12 @@ std::optional<std::unordered_set<OperInfrastInfo>> asst::IdentifyOperTask::swipe
|
||||
OperInfrastInfo info;
|
||||
info.elite = elite;
|
||||
info.name = textarea.text;
|
||||
detected_opers.emplace(std::move(info));
|
||||
detected_opers.emplace(textarea.text, std::move(info));
|
||||
}
|
||||
|
||||
json::value opers_json;
|
||||
std::vector<json::value> opers_json_vec;
|
||||
for (const OperInfrastInfo& info : detected_opers) {
|
||||
for (const auto& [name, info] : detected_opers) {
|
||||
json::value info_json;
|
||||
info_json["name"] = Utf8ToGbk(info.name);
|
||||
info_json["elite"] = info.elite;
|
||||
@@ -149,7 +149,11 @@ std::optional<std::unordered_set<OperInfrastInfo>> asst::IdentifyOperTask::swipe
|
||||
return detected_opers;
|
||||
}
|
||||
|
||||
std::vector<TextArea> asst::IdentifyOperTask::detect_opers(const cv::Mat& image, std::unordered_map<std::string, std::string>& feature_cond, std::unordered_set<std::string>& feature_whatever)
|
||||
std::vector<TextArea> asst::IdentifyOperTask::detect_opers(
|
||||
const cv::Mat& image,
|
||||
std::unordered_map<std::string,
|
||||
std::string>& feature_cond,
|
||||
std::unordered_set<std::string>& feature_whatever)
|
||||
{
|
||||
// 裁剪出来干员名的一个长条形图片,没必要把整张图片送去识别
|
||||
int cropped_height = image.rows * m_cropped_height_ratio;
|
||||
|
||||
@@ -16,11 +16,14 @@ namespace asst {
|
||||
constexpr static int SwipeDuration = 2000;
|
||||
constexpr static int SwipeExtraDelay = 1000;
|
||||
|
||||
std::optional<std::unordered_set<OperInfrastInfo>> swipe_and_detect();
|
||||
// 一边滑动一边识别
|
||||
virtual std::optional<std::unordered_map<std::string, OperInfrastInfo>> swipe_and_detect();
|
||||
|
||||
// 检测干员名
|
||||
std::vector<TextArea> detect_opers(const cv::Mat& image,
|
||||
std::unordered_map<std::string, std::string>& feature_cond,
|
||||
std::unordered_set<std::string>& feature_whatever);
|
||||
// 检测干员精英化等级
|
||||
int detect_elite(const cv::Mat& image, const asst::Rect name_rect);
|
||||
|
||||
bool swipe(bool reverse = false);
|
||||
|
||||
@@ -30,7 +30,8 @@ bool InfrastConfiger::parse(json::value&& json)
|
||||
for (json::value& facility : root["infrast"].as_array())
|
||||
{
|
||||
std::string key = facility["facility"].as_string();
|
||||
std::vector< std::vector<OperInfrastInfo>> combs_vec;
|
||||
|
||||
std::vector<std::vector<OperInfrastInfo>> combs_vec;
|
||||
for (json::value& comb : facility["combs"].as_array())
|
||||
{
|
||||
std::vector<OperInfrastInfo> opers;
|
||||
@@ -43,7 +44,13 @@ bool InfrastConfiger::parse(json::value&& json)
|
||||
}
|
||||
combs_vec.emplace_back(std::move(opers));
|
||||
}
|
||||
m_infrast_combs.emplace(std::move(key), std::move(combs_vec));
|
||||
m_infrast_combs.emplace(key, std::move(combs_vec));
|
||||
|
||||
std::vector<std::string> end_name_vec;
|
||||
for (json::value& name : facility["endFlag"].as_array()) {
|
||||
end_name_vec.emplace_back(name.as_string());
|
||||
}
|
||||
m_infrast_end_flag.emplace(key, std::move(end_name_vec));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -22,8 +22,10 @@ namespace asst {
|
||||
std::unordered_set<std::string> m_all_opers_name; // 所有干员的名字
|
||||
std::unordered_map<std::string, std::string> m_oper_name_feat; // 根据关键字需要特征检测干员名:如果OCR识别到了key的内容但是却没有value的内容,则进行特征检测进一步确认
|
||||
std::unordered_set<std::string> m_oper_name_feat_whatever; // 无论如何都进行特征检测的干员名
|
||||
|
||||
std::unordered_map<std::string, std::vector<std::vector<OperInfrastInfo>>> m_infrast_combs; // 各个设施内的可能干员组合,注意值是vector,是有序的
|
||||
// 各个设施内的可能干员组合,注意值是vector,是有序的
|
||||
std::unordered_map<std::string, std::vector<std::vector<OperInfrastInfo>>> m_infrast_combs;
|
||||
// 各个设施的识别结束标记,识别到这个名字就停止继续识别了,一般放列表中最后几个人名
|
||||
std::unordered_map<std::string, std::vector<std::string>> m_infrast_end_flag;
|
||||
|
||||
protected:
|
||||
InfrastConfiger() = default;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <thread>
|
||||
#include <future>
|
||||
#include <algorithm>
|
||||
#include <future>
|
||||
|
||||
#include <opencv2/opencv.hpp>
|
||||
|
||||
@@ -35,15 +36,97 @@ bool asst::InfrastStationTask::run()
|
||||
m_swipe_begin = Rect(width * 0.9, height * 0.5, 0, 0);
|
||||
m_swipe_end = Rect(width * 0.4, 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::vector<std::string> optimal_comb = calc_optimal_comb(cur_opers_info);
|
||||
bool select_ret = swipe_and_select(optimal_comb);
|
||||
|
||||
return select_ret;
|
||||
}
|
||||
|
||||
std::optional<std::unordered_map<std::string, OperInfrastInfo>> asst::InfrastStationTask::swipe_and_detect()
|
||||
{
|
||||
|
||||
std::unordered_map<std::string, std::string> feature_cond = InfrastConfiger::get_instance().m_oper_name_feat;
|
||||
std::unordered_set<std::string> feature_whatever = InfrastConfiger::get_instance().m_oper_name_feat_whatever;
|
||||
|
||||
std::vector<std::string> end_flag_vec = InfrastConfiger::get_instance().m_infrast_end_flag[m_facility];
|
||||
|
||||
std::unordered_map<std::string, OperInfrastInfo> cur_opers_info;
|
||||
// 因为有些干员在宿舍休息,或者被其他基建占用了,所以需要重新识别一遍当前可用的干员
|
||||
for (int i = 0; i != SwipeMaxTimes; ++i) {
|
||||
const cv::Mat& image = get_format_image(true);
|
||||
// 异步进行滑动操作
|
||||
std::future<bool> swipe_future = std::async(
|
||||
std::launch::async, &InfrastStationTask::swipe, this, false);
|
||||
|
||||
auto cur_name_textarea = detect_opers(image, feature_cond, feature_whatever);
|
||||
for (const TextArea& textarea : cur_name_textarea) {
|
||||
OperInfrastInfo info;
|
||||
// 考虑map中没有这个名字的情况:包括一开始识别漏了、抽到了新干员但没更新等,也有可能是本次识别错了
|
||||
// TODO,这里可以抛个回调出去,提示这种case
|
||||
if (m_all_opers_info.find(textarea.text) == m_all_opers_info.cend()) {
|
||||
info.name = textarea.text;
|
||||
info.elite = 0;
|
||||
info.level = 0;
|
||||
}
|
||||
else {
|
||||
info = m_all_opers_info[textarea.text];
|
||||
}
|
||||
cur_opers_info.emplace(textarea.text, std::move(info));
|
||||
}
|
||||
|
||||
json::value opers_json;
|
||||
std::vector<json::value> opers_json_vec;
|
||||
for (const auto& [name, info] : cur_opers_info) {
|
||||
json::value info_json;
|
||||
info_json["name"] = Utf8ToGbk(info.name);
|
||||
info_json["elite"] = info.elite;
|
||||
//info_json["level"] = info.level;
|
||||
opers_json_vec.emplace_back(std::move(info_json));
|
||||
}
|
||||
opers_json["all"] = json::array(opers_json_vec);
|
||||
m_callback(AsstMsg::OpersDetected, opers_json, m_callback_arg);
|
||||
|
||||
bool break_flag = false;
|
||||
// 如果找到了end_flag_vec中的名字,说明已经识别到有当前基建技能的最后一个干员了,就不用接着识别了
|
||||
auto find_iter = std::find_first_of(
|
||||
cur_opers_info.cbegin(), cur_opers_info.cend(),
|
||||
end_flag_vec.cbegin(), end_flag_vec.cend(),
|
||||
[](const auto& lhs, const auto& rhs) ->bool {
|
||||
return lhs.first == rhs;
|
||||
});
|
||||
if (find_iter != cur_opers_info.cend()) {
|
||||
break_flag = true;
|
||||
}
|
||||
// 阻塞等待异步的滑动结束
|
||||
if (!swipe_future.get()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (break_flag) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return cur_opers_info;
|
||||
}
|
||||
|
||||
std::vector<std::string> asst::InfrastStationTask::calc_optimal_comb(
|
||||
const std::unordered_map<std::string, OperInfrastInfo>& cur_opers_info) const
|
||||
{
|
||||
// 配置文件中的干员组合,和抓出来的干员名比对,如果组合中的干员都有,那就用这个组合
|
||||
// 注意配置中的干员组合需要是有序的
|
||||
// Todo 时间复杂度起飞了,需要优化下
|
||||
std::vector<std::string> optimal_comb; // OperInfrastInfo是带精英化和等级信息的,基建里识别不到,也用不到,这里只保留干员名
|
||||
for (const auto& name_vec : InfrastConfiger::get_instance().m_infrast_combs[m_facility]) {
|
||||
int count = 0;
|
||||
std::vector<std::string> temp_comb;
|
||||
for (const OperInfrastInfo& info : name_vec) {
|
||||
if (m_all_opers_info.find(info) != m_all_opers_info.cend()) {
|
||||
// 找到了干员名,而且当前精英化等级需要大于等于配置文件中要求的精英化等级
|
||||
if (cur_opers_info.find(info.name) != cur_opers_info.cend()
|
||||
&& cur_opers_info.at(info.name).elite >= info.elite) {
|
||||
++count;
|
||||
temp_comb.emplace_back(info.name);
|
||||
}
|
||||
@@ -51,43 +134,44 @@ bool asst::InfrastStationTask::run()
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (count == temp_comb.size()) {
|
||||
if (count != 0 && count == temp_comb.size()) {
|
||||
optimal_comb = temp_comb;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> optimal_comb_gbk; // 给回调json用的,gbk的
|
||||
for (const std::string& name : optimal_comb)
|
||||
{
|
||||
for (const std::string& name : optimal_comb) {
|
||||
optimal_comb_gbk.emplace_back(Utf8ToGbk(name));
|
||||
}
|
||||
|
||||
json::value opers_json;
|
||||
opers_json["comb"] = json::array(optimal_comb_gbk);
|
||||
m_callback(AsstMsg::InfrastComb, opers_json, m_callback_arg);
|
||||
|
||||
return optimal_comb;
|
||||
}
|
||||
|
||||
bool asst::InfrastStationTask::swipe_and_select(std::vector<std::string>& name_comb, int swipe_max_times)
|
||||
{
|
||||
std::unordered_map<std::string, std::string> feature_cond = InfrastConfiger::get_instance().m_oper_name_feat;
|
||||
std::unordered_set<std::string> feature_whatever = InfrastConfiger::get_instance().m_oper_name_feat_whatever;
|
||||
|
||||
// 一边滑动一边点击最优解中的干员
|
||||
for (int i = 0; i != SwipeMaxTimes; ++i) {
|
||||
for (int i = 0; i != swipe_max_times; ++i) {
|
||||
const cv::Mat& image = get_format_image(true);
|
||||
auto cur_name_textarea = detect_opers(image, feature_cond, feature_whatever);
|
||||
|
||||
for (TextArea& text_area : cur_name_textarea) {
|
||||
// 点过了就不会再点了,直接从最优解vector里面删了
|
||||
auto iter = std::find(optimal_comb.begin(), optimal_comb.end(), text_area.text);
|
||||
if (iter != optimal_comb.end()) {
|
||||
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);
|
||||
optimal_comb.erase(iter);
|
||||
name_comb.erase(iter);
|
||||
}
|
||||
}
|
||||
if (optimal_comb.empty()) {
|
||||
if (name_comb.empty()) {
|
||||
break;
|
||||
}
|
||||
// 因为滑动和点击是矛盾的,这里没法异步做
|
||||
if (!swipe()) {
|
||||
if (!swipe(true)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,19 +12,23 @@ namespace asst {
|
||||
|
||||
virtual bool run() override;
|
||||
|
||||
void set_facility(std::string facility)
|
||||
{
|
||||
void set_facility(std::string facility) {
|
||||
m_facility = std::move(facility);
|
||||
}
|
||||
void set_all_opers_info(std::unordered_set<OperInfrastInfo> infos) {
|
||||
void set_all_opers_info(std::unordered_map<std::string, OperInfrastInfo> infos) {
|
||||
m_all_opers_info = std::move(infos);
|
||||
}
|
||||
protected:
|
||||
constexpr static int SwipeMaxTimes = 17;
|
||||
|
||||
//std::vector<std::string> calc_optimal_comb();
|
||||
// 一边滑动一边识别
|
||||
virtual std::optional<std::unordered_map<std::string, OperInfrastInfo>> swipe_and_detect() override;
|
||||
// 计算最优解干员组合
|
||||
std::vector<std::string> calc_optimal_comb(const std::unordered_map<std::string, OperInfrastInfo>& cur_opers_info) const;
|
||||
// 一边滑动一边识别并点击干员名
|
||||
bool swipe_and_select(std::vector<std::string>& name_comb, int swipe_max_times = SwipeMaxTimes);
|
||||
|
||||
std::string m_facility; // 设施名(制造站、贸易站、控制中枢……)
|
||||
std::unordered_set<OperInfrastInfo> m_all_opers_info;
|
||||
std::unordered_map<std::string, OperInfrastInfo> m_all_opers_info;
|
||||
};
|
||||
}
|
||||
@@ -38,13 +38,17 @@
|
||||
],
|
||||
"efficiency": 90
|
||||
}
|
||||
]
|
||||
],
|
||||
"endFlag": [ "月见夜", "炎熔" ],
|
||||
"endFlag_Doc": "识别到这里面的名字,就不继续往后识别了,一般用列表里的最后一个人名"
|
||||
},
|
||||
{
|
||||
"facility": "Trade",
|
||||
"Doc": "贸易站",
|
||||
"combs": [
|
||||
]
|
||||
],
|
||||
"endFlag": [ ],
|
||||
"endFlag_Doc": "识别到这里面的名字,就不继续往后识别了,一般用列表里的最后一个人名"
|
||||
}
|
||||
],
|
||||
"featureKey_Doc": "特征检测的关键字,如果OCR识别到了左边的内容,但是却没有右边的内容,则进行特征检测进一步确认",
|
||||
|
||||
Reference in New Issue
Block a user