refactor.重构基建技能相关结构,调整干员hash算法

This commit is contained in:
MistEO
2021-11-16 01:02:20 +08:00
parent 6a54afab24
commit 5c4aa2464b
7 changed files with 186 additions and 147 deletions

View File

@@ -355,11 +355,11 @@
"all": 30
},
"hash": {
"史都华德": "43174f1fefbae7abcfd3ce13e23bef3bcf0fcf0fcff24ff7ef04fe06be060a00",
"杰西卡": "27c427c4ffc6f88663c473df70dfb0dfaac4aac622c6e047ebc4ebc4cbc40844",
"调香师": "bbcfbb9f0a1a3bda9bdf999fbadf3a5f3fdf3bdf3a4f7bcfea52cbd2cbd20a42",
"香草": "7cff78ff5024fe24fe7e7c427c7ed67efe7e7c7e44187cff44187c187c184418",
"罗比菈塔": "e8f1e8f708f70961ed2eeeff0ef3eaf7a857a9d369f769af4d29cbf38bf38081"
"史都华德": "43774f3fefbfefafe7efcff7ef3fef3fef3fcf0fcb0fcff74bf7ef05fe07be07",
"杰西卡": "200427e427e7ffe7fbc473c473df73dfbbc4abc6aac62247ca45dbc4dbc4dbc4",
"调香师": "b8cfbbcfbbdf9b1a3fffbfffbb9fbbdfbfffbffffbdfebcffbd7ebd7cbd2cbd2",
"香草": "1e247eff7cff70fffe7efe7e7c7e7e7eff7eff7e7e7e7e187eff7eff7e187e18",
"罗比菈塔": "e8f3e8f708f70963e96feefe6ef7eaf7e817a857a9f76daf4d29cf29cff38bf3"
}
},
{
@@ -371,8 +371,8 @@
"all": 20
},
"hash": {
"夜刀": "1000fe7efe7e68125e105e125a12da12fe32fc225c224c625e627ece72ce6000",
"流星": "887efe7e7e427e7e9642be7ebe7e3a584c7e4cfe4cfe6cfeac18eeffeeff0200"
"夜刀": "10001800ff7fff7f78197f195f195f19fe11fe31fe315c315e615f677fe773c6",
"流星": "c87eff7e7f7e7f7e1e7efe7eff7e7f781e7e5e7e5efe5e7eff7eff18afffafff"
}
}
]

View File

@@ -858,13 +858,13 @@
"InfrastSkillsHash": {
"template": "empty.png",
"templThreshold_Doc": "作为哈希距离的阈值使用",
"templThreshold": 60,
"templThreshold": 55,
"rectMove_Doc": "基于笑脸的位置移动",
"rectMove": [
0,
21,
20,
113,
20
22
]
},
"InfrastSmileyOnRest": {

View File

@@ -67,7 +67,18 @@ namespace asst
{
namespace infrast
{
// 基建单个干员的技能
struct Oper
{
::std::string hash; // 有些干员的技能是完全一样的做个hash区分一下不同干员
::std::string name; // 预留
Smiley smiley;
double mood_ratio = 0; // 心情进度条的百分比
Doing doing = Doing::Invalid;
bool selected = false; // 干员是否已被选择(蓝色的选择框)
::std::unordered_set<Skill> skills;
Rect rect;
};
struct SkillsComb
{
SkillsComb() = default;
@@ -92,34 +103,18 @@ namespace asst
::std::unordered_set<Skill> skills;
::std::unordered_map<std::string, double> efficient;
::std::unordered_map<std::string, ::std::string> efficient_regex;
};
struct Oper
{
::std::string hash; // 有些干员的技能是完全一样的做个hash区分一下不同干员
::std::string name; // 预留
Smiley smiley;
double mood_ratio = 0; // 心情进度条的百分比
Doing doing = Doing::Invalid;
bool selected = false; // 干员是否已被选择(蓝色的选择框)
SkillsComb skills_comb;
Rect rect;
};
struct SkillsCombWithCond
{
SkillsComb skills_comb;
::std::string hash;
bool hash_filter = false;
::std::unordered_map<std::string, ::std::string> hashs; // 限定只允许某些hash匹配的某些干员。若hash不相同即使技能匹配了也不可用。hashs若为空则不生效
::std::unordered_map<std::string, ::std::string> possible_hashs; // 限定只允许某些hash匹配的某些干员。若hash不相同即使技能匹配了也不可用。hashs若为空则不生效
};
// 基建技能组
struct SkillsGroup
{
::std::string desc; // 文字介绍,实际不起作用
::std::unordered_map<std::string, int> conditions; // 技能组合可用条件例如key 发电站数量value 3
::std::vector<SkillsCombWithCond> necessary; // 必选技能。这里面的缺少任一,则该技能组合不可用
::std::vector<SkillsCombWithCond> optional; // 可选技能。
::std::vector<SkillsComb> necessary; // 必选技能。这里面的缺少任一,则该技能组合不可用
::std::vector<SkillsComb> optional; // 可选技能。
bool allow_external = false; // 当干员数没满3个的时候是否允许补充外部干员
};

View File

@@ -87,8 +87,7 @@ bool asst::InfrastConfiger::parse(const json::value& json)
}
}
for (const json::value& necessary_json : group_json.at("necessary").as_array()) {
infrast::SkillsCombWithCond comb_with_cond;
infrast::SkillsComb& comb = comb_with_cond.skills_comb;
infrast::SkillsComb comb;
comb.desc = necessary_json.get("desc", std::string());
for (const json::value& skill_json : necessary_json.at("skills").as_array()) {
const auto& skill = m_skills.at(facility_name).at(skill_json.as_string());
@@ -135,15 +134,15 @@ bool asst::InfrastConfiger::parse(const json::value& json)
}
}
if (necessary_json.exist("hash")) {
comb.hash_filter = true;
for (const auto& [key, value] : necessary_json.at("hash").as_object()) {
comb_with_cond.hashs.emplace(key, value.as_string());
comb.possible_hashs.emplace(key, value.as_string());
}
}
group.necessary.emplace_back(std::move(comb_with_cond));
group.necessary.emplace_back(std::move(comb));
}
for (const json::value& opt_json : group_json.at("optional").as_array()) {
infrast::SkillsCombWithCond comb_with_cond;
infrast::SkillsComb& comb = comb_with_cond.skills_comb;
infrast::SkillsComb comb;
comb.desc = opt_json.get("desc", std::string());
for (const json::value& skill_json : opt_json.at("skills").as_array()) {
const auto& skill = m_skills.at(facility_name).at(skill_json.as_string());
@@ -190,11 +189,12 @@ bool asst::InfrastConfiger::parse(const json::value& json)
}
}
if (opt_json.exist("hash")) {
comb.hash_filter = true;
for (const auto& [key, value] : opt_json.at("hash").as_object()) {
comb_with_cond.hashs.emplace(key, value.as_string());
comb.possible_hashs.emplace(key, value.as_string());
}
}
group.optional.emplace_back(std::move(comb_with_cond));
group.optional.emplace_back(std::move(comb));
}
group_vec.emplace_back(std::move(group));
}

View File

@@ -204,35 +204,46 @@ void asst::InfrastOperImageAnalyzer::hash_analyze()
const Rect hash_rect_move = resource.task().task_ptr("InfrastSkillsHash")->rect_move;
cv::Mat gray;
cv::cvtColor(m_image, gray, cv::COLOR_BGR2GRAY);
for (auto&& oper : m_result) {
Rect roi = hash_rect_move;
roi.x += oper.smiley.rect.x;
roi.y += oper.smiley.rect.y;
// 从左往右找到第一个白色点
Rect white_roi = roi;
constexpr static int HashKernelSize = 16;
int threshold = 200;
bool find_point = false;
for (int i = 0; i != white_roi.width && !find_point; ++i) {
for (int j = 0; j != white_roi.height && !find_point; ++j) {
cv::Point point(white_roi.x + i, white_roi.y + j);
auto value = m_image.at<cv::Vec3b>(point);
if (value[0] > threshold && value[1] > threshold && value[2] > threshold) {
white_roi.x += i;
white_roi.width -= i;
find_point = true;
break;
constexpr static int threshold = 100;
auto check_point = [&](cv::Point point) -> bool {
auto value = gray.at<uchar>(point);
return value > threshold;
};
// 找到四个方向上最靠外的白色点把ROI缩小裁出来
int left = -1, right = -1, top = INT_MAX, bottom = -1;
for (int i = 0; i != roi.width; ++i) {
for (int j = 0; j != roi.height; ++j) {
cv::Point point(roi.x + i, roi.y + j);
if (check_point(point)) {
if (left < 0) {
left = i;
}
right = i;
top = (std::min)(top, j);
bottom = (std::max)(bottom, j);
}
}
}
cv::Mat image_roi = m_image(utils::make_rect<cv::Rect>(white_roi));
roi.x += left;
roi.width = right - left + 1;
roi.y += top;
roi.height = bottom - top + 1;
cv::Mat image_roi = gray(utils::make_rect<cv::Rect>(roi));
cv::Mat bin;
cv::cvtColor(image_roi, image_roi, cv::COLOR_BGR2GRAY);
cv::threshold(image_roi, bin, threshold, 255, cv::THRESH_BINARY);
constexpr static int HashKernelSize = 16;
cv::resize(bin, bin, cv::Size(HashKernelSize, HashKernelSize));
std::stringstream hash_value;
uchar* pix = bin.data;
cv::uint8_t* pix = bin.data;
int tmp_dec = 0;
for (int ro = 0; ro < 256; ro++) {
tmp_dec = tmp_dec << 1;
@@ -359,7 +370,7 @@ void asst::InfrastOperImageAnalyzer::skill_analyze()
#ifdef LOG_TRACE
cv::Mat skill_mat = m_image(utils::make_rect<cv::Rect>(skill_rect));
#endif
oper.skills_comb.skills.emplace(std::move(most_confident_skills));
oper.skills.emplace(std::move(most_confident_skills));
}
log.trace(log_str, "]");
}

View File

@@ -164,31 +164,43 @@ size_t asst::InfrastProductionTask::opers_detect()
LogTraceFunction;
const auto& image = ctrler.get_image();
InfrastOperImageAnalyzer skills_analyzer(image);
skills_analyzer.set_facility(m_facility);
InfrastOperImageAnalyzer oper_analyzer(image);
oper_analyzer.set_facility(m_facility);
if (!skills_analyzer.analyze()) {
if (!oper_analyzer.analyze()) {
return 0;
}
const auto& cur_all_info = skills_analyzer.get_result();
max_num_of_opers_per_page = (std::max)(max_num_of_opers_per_page, cur_all_info.size());
const auto& cur_all_opers = oper_analyzer.get_result();
max_num_of_opers_per_page = (std::max)(max_num_of_opers_per_page, cur_all_opers.size());
for (const auto& cur_info : cur_all_info) {
int cur_available_num = cur_all_opers.size();
for (const auto& cur_oper : cur_all_opers) {
if (cur_oper.skills.empty()) {
--cur_available_num;
continue;
}
auto find_iter = std::find_if(
m_all_available_opers.cbegin(), m_all_available_opers.cend(),
[&cur_info](const infrast::Oper& info) -> bool {
int dist = utils::hamming(cur_info.hash, info.hash);
return dist < m_hash_dist_threshold;
[&cur_oper](const infrast::Oper& oper) -> bool {
// 技能相同的有可能是同一个干员比一下hash
if (oper.skills == cur_oper.skills) {
int dist = utils::hamming(cur_oper.hash, oper.hash);
#ifdef LOG_TRACE
log.trace("opers_detect hash dist |", dist, cur_oper.hash, oper.hash);
#endif
return dist < m_hash_dist_threshold;
}
else { // 技能不同肯定不是同一个干员,不比了
return false;
}
});
// 如果两个的hash距离过小则认为是同一个干员不进行插入
if (find_iter != m_all_available_opers.cend()) {
continue;
}
auto pred_info = cur_info;
pred_info.skills_comb = efficient_regex_calc(pred_info.skills_comb);
m_all_available_opers.emplace_back(std::move(pred_info));
m_all_available_opers.emplace_back(cur_oper);
}
return cur_all_info.size();
return cur_available_num;
}
bool asst::InfrastProductionTask::optimal_calc()
@@ -200,33 +212,40 @@ bool asst::InfrastProductionTask::optimal_calc()
if (m_all_available_opers.size() < max_num_of_opers) {
return false;
}
std::vector<infrast::SkillsComb> all_avaliable_combs;
all_avaliable_combs.reserve(m_all_available_opers.size());
for (auto&& oper : m_all_available_opers) {
auto comb = efficient_regex_calc(oper.skills);
comb.hash = oper.hash;
all_avaliable_combs.emplace_back(std::move(comb));
}
// 先把单个的技能按效率排个序,取效率最高的几个
std::vector<infrast::Oper> optimal_opers;
optimal_opers.reserve(max_num_of_opers);
std::vector<infrast::SkillsComb> optimal_combs;
optimal_combs.reserve(max_num_of_opers);
double max_efficient = 0;
std::sort(m_all_available_opers.begin(), m_all_available_opers.end(),
[&](const infrast::Oper& lhs, const infrast::Oper& rhs) -> bool {
return lhs.skills_comb.efficient.at(m_product) > rhs.skills_comb.efficient.at(m_product);
std::sort(all_avaliable_combs.begin(), all_avaliable_combs.end(),
[&](const infrast::SkillsComb& lhs, const infrast::SkillsComb& rhs) -> bool {
return lhs.efficient.at(m_product) > rhs.efficient.at(m_product);
});
for (const auto& oper : m_all_available_opers) {
for (const auto& comb : all_avaliable_combs) {
std::string skill_str;
for (const auto& skill : oper.skills_comb.skills) {
for (const auto& skill : comb.skills) {
skill_str += skill.id + " ";
}
log.trace(skill_str, oper.skills_comb.efficient.at(m_product));
log.trace(skill_str, comb.efficient.at(m_product));
}
for (int i = 0; i != max_num_of_opers; ++i) {
optimal_opers.emplace_back(m_all_available_opers.at(i));
max_efficient += m_all_available_opers.at(i).skills_comb.efficient.at(m_product);
optimal_combs.emplace_back(all_avaliable_combs.at(i));
max_efficient += all_avaliable_combs.at(i).efficient.at(m_product);
}
{
std::string log_str = "[ ";
for (const auto& oper : optimal_opers) {
log_str += oper.skills_comb.desc.empty() ? oper.skills_comb.skills.begin()->names.front() : oper.skills_comb.desc;
for (const auto& comb : optimal_combs) {
log_str += comb.desc.empty() ? comb.skills.begin()->names.front() : comb.desc;
log_str += "; ";
}
log_str += "]";
@@ -237,10 +256,10 @@ bool asst::InfrastProductionTask::optimal_calc()
auto& all_group = resource.infrast().get_skills_group(m_facility);
for (const infrast::SkillsGroup& group : all_group) {
LogTraceScope(group.desc);
auto cur_available_opers = m_all_available_opers;
auto cur_available_opers = all_avaliable_combs;
bool group_unavailable = false;
std::vector<infrast::Oper> cur_opers;
cur_opers.reserve(max_num_of_opers);
std::vector<infrast::SkillsComb> cur_combs;
cur_combs.reserve(max_num_of_opers);
double cur_efficient = 0;
// 条件判断,不符合的直接过滤掉
bool meet_condition = true;
@@ -264,23 +283,24 @@ bool asst::InfrastProductionTask::optimal_calc()
continue;
}
// necessary里的技能一个都不能少
for (const infrast::SkillsCombWithCond& nec_skills : group.necessary) {
// TODO necessary暂时没做hash校验。因为没有需要比hash的necessary干员
for (const infrast::SkillsComb& nec_skills : group.necessary) {
auto find_iter = std::find_if(
cur_available_opers.cbegin(), cur_available_opers.cend(),
[&](const infrast::Oper& arg) -> bool {
return arg.skills_comb == nec_skills.skills_comb;
[&](const infrast::SkillsComb& arg) -> bool {
return arg == nec_skills;
});
if (find_iter == cur_available_opers.cend()) {
group_unavailable = true;
break;
}
cur_opers.emplace_back(*find_iter);
if (auto iter = nec_skills.skills_comb.efficient_regex.find(m_product);
iter != nec_skills.skills_comb.efficient_regex.cend()) {
cur_efficient += efficient_regex_calc(nec_skills.skills_comb).efficient.at(m_product);
cur_combs.emplace_back(nec_skills);
if (auto iter = nec_skills.efficient_regex.find(m_product);
iter != nec_skills.efficient_regex.cend()) {
cur_efficient += efficient_regex_calc(nec_skills.skills).efficient.at(m_product);
}
else {
cur_efficient += nec_skills.skills_comb.efficient.at(m_product);
cur_efficient += nec_skills.efficient.at(m_product);
}
cur_available_opers.erase(find_iter);
}
@@ -290,32 +310,32 @@ bool asst::InfrastProductionTask::optimal_calc()
// 排个序,因为产物不同,效率可能会发生变化,所以配置文件里默认的顺序不一定准确
auto optional = group.optional;
for (auto&& opt : optional) {
if (auto iter = opt.skills_comb.efficient_regex.find(m_product);
iter != opt.skills_comb.efficient_regex.cend()) {
opt.skills_comb = efficient_regex_calc(opt.skills_comb);
if (auto iter = opt.efficient_regex.find(m_product);
iter != opt.efficient_regex.cend()) {
opt = efficient_regex_calc(opt.skills);
}
}
std::sort(optional.begin(), optional.end(),
[&](const infrast::SkillsCombWithCond& lhs,
const infrast::SkillsCombWithCond& rhs) -> bool {
return lhs.skills_comb.efficient.at(m_product) > rhs.skills_comb.efficient.at(m_product);
[&](const infrast::SkillsComb& lhs,
const infrast::SkillsComb& rhs) -> bool {
return lhs.efficient.at(m_product) > rhs.efficient.at(m_product);
});
// 可能有多个干员有同样的技能,所以这里需要循环找同一个技能,直到找不到为止
for (const infrast::SkillsCombWithCond& opt : optional) {
for (const infrast::SkillsComb& opt : optional) {
auto find_iter = cur_available_opers.cbegin();
while (cur_opers.size() != max_num_of_opers) {
while (cur_combs.size() != max_num_of_opers) {
find_iter = std::find_if(
find_iter, cur_available_opers.cend(),
[&](const infrast::Oper& arg) -> bool {
return arg.skills_comb.skills == opt.skills_comb.skills;
[&](const infrast::SkillsComb& arg) -> bool {
return arg == opt;
});
if (find_iter != cur_available_opers.cend()) {
// 要求技能匹配的同时hash也要匹配
bool hash_matched = false;
if (!opt.hashs.empty()) {
for (const auto& [key, hash] : opt.hashs) {
if (!opt.possible_hashs.empty()) {
for (const auto& [key, hash] : opt.possible_hashs) {
int dist = utils::hamming(find_iter->hash, hash);
log.trace("optimal_calc | hash dist", dist, hash, find_iter->hash);
if (dist < m_hash_dist_threshold) {
@@ -332,8 +352,8 @@ bool asst::InfrastProductionTask::optimal_calc()
continue;
}
cur_opers.emplace_back(*find_iter);
cur_efficient += opt.skills_comb.efficient.at(m_product);
cur_combs.emplace_back(opt);
cur_efficient += opt.efficient.at(m_product);
find_iter = cur_available_opers.erase(find_iter);
}
else {
@@ -343,12 +363,12 @@ bool asst::InfrastProductionTask::optimal_calc()
}
// 说明可选的没凑满人
if (cur_opers.size() < max_num_of_opers) {
if (cur_combs.size() < max_num_of_opers) {
// 允许外部的话,就把单个干员凑进来
if (group.allow_external) {
for (size_t i = cur_opers.size(); i != max_num_of_opers; ++i) {
cur_opers.emplace_back(cur_available_opers.at(i));
cur_efficient += cur_available_opers.at(i).skills_comb.efficient.at(m_product);
for (size_t i = cur_combs.size(); i != max_num_of_opers; ++i) {
cur_combs.emplace_back(cur_available_opers.at(i));
cur_efficient += cur_available_opers.at(i).efficient.at(m_product);
}
}
else { // 否则这个组合人不够,就不可用了
@@ -357,8 +377,8 @@ bool asst::InfrastProductionTask::optimal_calc()
}
{
std::string log_str = "[ ";
for (const auto& oper : cur_opers) {
log_str += oper.skills_comb.desc.empty() ? oper.skills_comb.skills.begin()->names.front() : oper.skills_comb.desc;
for (const auto& comb : cur_combs) {
log_str += comb.desc.empty() ? comb.skills.begin()->names.front() : comb.desc;
log_str += "; ";
}
log_str += "]";
@@ -366,21 +386,21 @@ bool asst::InfrastProductionTask::optimal_calc()
}
if (cur_efficient > max_efficient) {
optimal_opers = std::move(cur_opers);
optimal_combs = std::move(cur_combs);
max_efficient = cur_efficient;
}
}
{
std::string log_str = "[ ";
for (const auto& oper : optimal_opers) {
log_str += oper.skills_comb.desc.empty() ? oper.skills_comb.skills.begin()->names.front() : oper.skills_comb.desc;
for (const auto& comb : optimal_combs) {
log_str += comb.desc.empty() ? comb.skills.begin()->names.front() : comb.desc;
log_str += "; ";
}
log_str += "]";
log.trace("optimal efficient", max_efficient, " , skills:", log_str);
}
m_optimal_opers = std::move(optimal_opers);
m_optimal_combs = std::move(optimal_combs);
return true;
}
@@ -395,18 +415,18 @@ bool asst::InfrastProductionTask::opers_choose()
}
const auto& image = ctrler.get_image();
InfrastOperImageAnalyzer skills_analyzer(image);
skills_analyzer.set_facility(m_facility);
InfrastOperImageAnalyzer oper_analyzer(image);
oper_analyzer.set_facility(m_facility);
if (!skills_analyzer.analyze()) {
if (!oper_analyzer.analyze()) {
return false;
}
skills_analyzer.sort_by_loc();
oper_analyzer.sort_by_loc();
auto cur_all_info = skills_analyzer.get_result();
auto cur_all_opers = oper_analyzer.get_result();
// 这个情况一般是滑动/识别出错了,把所有的干员都滑过去了
if (cur_all_info.empty()) {
if (cur_all_opers.empty()) {
if (!has_error) {
has_error = true;
// 倒回去再来一遍
@@ -420,38 +440,49 @@ bool asst::InfrastProductionTask::opers_choose()
}
std::vector<std::string> selected_hash;
for (auto opt_iter = m_optimal_opers.begin(); opt_iter != m_optimal_opers.end();) {
auto find_iter = std::find_if(
cur_all_info.cbegin(), cur_all_info.cend(),
[&](const infrast::Oper& lhs) -> bool {
for (auto opt_iter = m_optimal_combs.begin(); opt_iter != m_optimal_combs.end();) {
auto oper_equal = [&](const infrast::Oper& lhs) -> bool {
if (lhs.skills != opt_iter->skills) {
return false;
}
if (!opt_iter->hash_filter) {
return true;
}
else {
// 既要技能相同也要hash相同双重校验
int dist = utils::hamming(lhs.hash, opt_iter->hash);
log.trace("opers_choose | hash dist", dist, lhs.hash, opt_iter->hash);
return dist < m_hash_dist_threshold
&& lhs.skills_comb == opt_iter->skills_comb;
});
if (find_iter == cur_all_info.cend()) {
for (const auto& [_, hash] : opt_iter->possible_hashs) {
int dist = utils::hamming(lhs.hash, hash);
log.trace("opers_choose | hash dist", dist, lhs.hash, hash);
if (dist < m_hash_dist_threshold) {
return true;
}
}
return false;
}
};
auto find_iter = std::find_if(
cur_all_opers.cbegin(), cur_all_opers.cend(), oper_equal);
if (find_iter == cur_all_opers.cend()) {
++opt_iter;
continue;
}
if (find_iter->selected == true) {
cur_all_info.erase(find_iter);
cur_all_opers.erase(find_iter);
continue;
}
ctrler.click(find_iter->rect);
selected_hash.emplace_back(find_iter->hash);
{
auto avlb_iter = std::find_if(
m_all_available_opers.cbegin(), m_all_available_opers.cend(),
[&](const infrast::Oper& lhs) -> bool {
return lhs.skills_comb == opt_iter->skills_comb;
});
m_all_available_opers.cbegin(), m_all_available_opers.cend(), oper_equal);
m_all_available_opers.erase(avlb_iter);
}
cur_all_info.erase(find_iter);
opt_iter = m_optimal_opers.erase(opt_iter);
cur_all_opers.erase(find_iter);
opt_iter = m_optimal_combs.erase(opt_iter);
}
if (m_optimal_opers.empty()) {
if (m_optimal_combs.empty()) {
break;
}
@@ -464,10 +495,11 @@ bool asst::InfrastProductionTask::opers_choose()
asst::infrast::SkillsComb
asst::InfrastProductionTask::efficient_regex_calc(
asst::infrast::SkillsComb skills_comb) const
std::unordered_set<infrast::Skill> skills) const
{
infrast::SkillsComb comb(std::move(skills));
// 根据正则,计算当前干员的实际效率
for (auto&& [product, formula] : skills_comb.efficient_regex) {
for (auto&& [product, formula] : comb.efficient_regex) {
std::string cur_formula = formula;
for (size_t pos = 0; pos != std::string::npos;) {
pos = cur_formula.find('[', pos);
@@ -489,9 +521,9 @@ asst::InfrastProductionTask::efficient_regex_calc(
}
int eff = calculator::eval(cur_formula);
skills_comb.efficient[product] = eff;
comb.efficient[product] = eff;
}
return skills_comb;
return comb;
}
bool asst::InfrastProductionTask::facility_list_detect()

View File

@@ -29,14 +29,15 @@ namespace asst
size_t opers_detect(); // 返回当前页面的干员数
bool optimal_calc();
bool opers_choose();
infrast::SkillsComb efficient_regex_calc(infrast::SkillsComb skill_info) const;
infrast::SkillsComb efficient_regex_calc(
std::unordered_set<infrast::Skill> skills) const;
static int m_hash_dist_threshold;
std::string m_facility;
std::string m_product;
std::vector<infrast::Oper> m_all_available_opers;
std::vector<infrast::Oper> m_optimal_opers;
std::vector<infrast::SkillsComb> m_optimal_combs;
std::vector<Rect> m_facility_list_tabs;
size_t max_num_of_opers_per_page = 0;
};