优化基建进驻,会先识别当前可用干员

This commit is contained in:
MistEO
2021-08-29 21:02:10 +08:00
parent 627b0bea65
commit f3f4298ab3
9 changed files with 146 additions and 37 deletions

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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;
};
}

View File

@@ -38,13 +38,17 @@
],
"efficiency": 90
}
]
],
"endFlag": [ "月见夜", "炎熔" ],
"endFlag_Doc": "识别到这里面的名字,就不继续往后识别了,一般用列表里的最后一个人名"
},
{
"facility": "Trade",
"Doc": "贸易站",
"combs": [
]
],
"endFlag": [ ],
"endFlag_Doc": "识别到这里面的名字,就不继续往后识别了,一般用列表里的最后一个人名"
}
],
"featureKey_Doc": "特征检测的关键字如果OCR识别到了左边的内容但是却没有右边的内容则进行特征检测进一步确认",