合并master和infrast,修改匹配算法参数

This commit is contained in:
MistEO
2021-08-15 00:48:09 +08:00
26 changed files with 797 additions and 258 deletions

View File

@@ -0,0 +1,68 @@
#include "InfrastConfiger.h"
#include <fstream>
#include <sstream>
#include <json.h>
#include "Logger.hpp"
using namespace asst;
bool InfrastConfiger::load(const std::string& filename)
{
DebugTraceFunction;
DebugTrace("Configer::load | ", filename);
InfrastConfiger temp;
if (temp._load(filename)) {
*this = std::move(temp);
return true;
}
else {
return false;
}
}
bool InfrastConfiger::_load(const std::string& filename)
{
std::ifstream ifs(filename, std::ios::in);
if (!ifs.is_open()) {
return false;
}
std::stringstream iss;
iss << ifs.rdbuf();
ifs.close();
std::string content(iss.str());
auto&& ret = json::parser::parse(content);
if (!ret) {
DebugTrace("parse error", content);
return false;
}
json::value root = ret.value();
try {
// ÖÆÔìÕ¾
for (json::value& comb_info : root["Manufacturing"]["combs"].as_array())
{
std::vector<std::string> comb_vec;
for (json::value& oper_name : comb_info["comb"].as_array())
{
std::string oper_name_str = oper_name.as_string();
m_mfg_opers.emplace(oper_name_str);
comb_vec.emplace_back(std::move(oper_name_str));
}
m_mfg_combs.emplace_back(std::move(comb_vec));
}
m_mfg_end = root["Manufacturing"]["end"].as_string();
// óÒ×Õ¾ TODO¡­¡­
}
catch (json::exception& e) {
DebugTraceError("Load config json error!", e.what());
return false;
}
DebugTrace("Load config succeed");
return true;
}

View File

@@ -26,25 +26,28 @@
<ClInclude Include="include\AsstPort.h" />
<ClInclude Include="include\Configer.h" />
<ClInclude Include="include\Identify.h" />
<ClInclude Include="include\InfrastConfiger.h" />
<ClInclude Include="include\Logger.hpp" />
<ClInclude Include="include\OpenRecruitConfiger.h" />
<ClInclude Include="include\RecruitConfiger.h" />
<ClInclude Include="include\Task.h" />
<ClInclude Include="include\Updater.h" />
<ClInclude Include="include\Version.h" />
<ClInclude Include="include\WinMacro.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="InfrastConfiger.cpp" />
<ClCompile Include="src\Assistance.cpp" />
<ClCompile Include="src\AsstCaller.cpp" />
<ClCompile Include="src\Configer.cpp" />
<ClCompile Include="src\Identify.cpp" />
<ClCompile Include="src\OpenRecruitConfiger.cpp" />
<ClCompile Include="src\RecruitConfiger.cpp" />
<ClCompile Include="src\Task.cpp" />
<ClCompile Include="src\Updater.cpp" />
<ClCompile Include="src\WinMacro.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="..\resource\operInfo.json" />
<None Include="..\resource\infrast.json" />
<None Include="..\resource\recruit.json" />
<None Include="..\resource\config.json" />
</ItemGroup>
<PropertyGroup Label="Globals">
@@ -155,7 +158,7 @@
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;CSHARP_API;MEO_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;MEO_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<LanguageStandard_C>stdc11</LanguageStandard_C>
@@ -196,7 +199,7 @@ xcopy /e /y /i $(SolutionDir)3rdPart\resource $(TargetDir)resource</Command>
</FunctionLevelLinking>
<IntrinsicFunctions>false</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;CSHARP_API;MEO_DLL_EXPORTS;LOG_TRACE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;MEO_DLL_EXPORTS;LOG_TRACE;DEBUG_API;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<LanguageStandard_C>stdc11</LanguageStandard_C>

View File

@@ -13,20 +13,23 @@
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="头文件\configer">
<UniqueIdentifier>{07a812fe-6b28-4be0-b6d4-33554824d3c5}</UniqueIdentifier>
</Filter>
<Filter Include="源文件\configer">
<UniqueIdentifier>{74bd94e1-4973-412a-a17a-d8457de387af}</UniqueIdentifier>
</Filter>
<Filter Include="头文件\type">
<UniqueIdentifier>{87c9dee5-847b-41e6-818e-387a6385ea8b}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\Configer.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="include\Identify.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="include\WinMacro.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="include\AsstDef.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="include\Assistance.h">
<Filter>头文件</Filter>
</ClInclude>
@@ -36,32 +39,35 @@
<ClInclude Include="include\Updater.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="include\Logger.hpp">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="include\AsstAux.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="include\AsstPort.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="include\OpenRecruitConfiger.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="include\Version.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="include\Task.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="include\InfrastConfiger.h">
<Filter>头文件\configer</Filter>
</ClInclude>
<ClInclude Include="include\RecruitConfiger.h">
<Filter>头文件\configer</Filter>
</ClInclude>
<ClInclude Include="include\Configer.h">
<Filter>头文件\configer</Filter>
</ClInclude>
<ClInclude Include="include\AsstDef.h">
<Filter>头文件\type</Filter>
</ClInclude>
<ClInclude Include="include\AsstAux.h">
<Filter>头文件\type</Filter>
</ClInclude>
<ClInclude Include="include\AsstPort.h">
<Filter>头文件\type</Filter>
</ClInclude>
<ClInclude Include="include\Version.h">
<Filter>头文件\type</Filter>
</ClInclude>
<ClInclude Include="include\Logger.hpp">
<Filter>头文件\type</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\Configer.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="src\Identify.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="src\WinMacro.cpp">
<Filter>源文件</Filter>
</ClCompile>
@@ -74,18 +80,30 @@
<ClCompile Include="src\Updater.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="src\OpenRecruitConfiger.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="src\Task.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="InfrastConfiger.cpp">
<Filter>源文件\configer</Filter>
</ClCompile>
<ClCompile Include="src\Identify.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="src\RecruitConfiger.cpp">
<Filter>源文件\configer</Filter>
</ClCompile>
<ClCompile Include="src\Configer.cpp">
<Filter>源文件\configer</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="..\resource\config.json">
<Filter>资源文件</Filter>
</None>
<None Include="..\resource\operInfo.json">
<None Include="..\resource\recruit.json">
<Filter>资源文件</Filter>
</None>
<None Include="..\resource\infrast.json">
<Filter>资源文件</Filter>
</None>
</ItemGroup>

View File

@@ -29,11 +29,14 @@ namespace asst {
// 开始刷理智
bool start_sanity();
// 开始访问基建
// 开始访问好友基建
bool start_visit();
// 开始公开招募操作
bool start_open_recruit(const std::vector<int>& required_level, bool set_time = true);
// 开始基建换班任务
bool start_infrast();
// 开始匹配任务,调试用
bool start_match_task(const std::string& task, int retry_times = ProcessTaskRetryTimesDefault, bool block = true);
// 开始OCR测试调试用
@@ -43,6 +46,9 @@ namespace asst {
bool set_param(const std::string& type, const std::string& param, const std::string& value);
// 滑动操作,调试用
bool swipe(const Point& p1, const Point& p2);
static constexpr int ProcessTaskRetryTimesDefault = 20;
static constexpr int OpenRecruitTaskRetyrTimesDefault = 5;

View File

@@ -18,10 +18,12 @@ extern "C" {
MEOAPI_PORT void AsstStop(asst::Assistance* p_asst);
MEOAPI_PORT bool AsstSetParam(asst::Assistance* p_asst, const char* type, const char* param, const char* value);
MEOAPI_PORT bool AsstRunOpenRecruit(asst::Assistance* p_asst, const int required_level[], bool set_time);
MEOAPI_PORT bool AsstStartInfrast(asst::Assistance* p_asst);
MEOAPI_PORT bool AsstTestOcr(asst::Assistance* p_asst, const char** text_array, int array_size, bool need_click);
MEOAPI_PORT bool CheckVersionUpdate(char* tag_buffer, int tag_bufsize, char* html_url_buffer, int html_bufsize, char* body_buffer, int body_bufsize);
MEOAPI_PORT bool AsstTestSwipe(asst::Assistance* p_asst, int x1, int y1, int x2, int y2);
#ifdef __cplusplus
}
#endif

View File

@@ -103,6 +103,7 @@ namespace asst {
std::string path;
std::string connect;
std::string click;
std::string swipe;
std::string display;
std::string display_regex;
int display_width = 0;

View File

@@ -2,7 +2,6 @@
#include <string>
#include <unordered_map>
#include <optional>
#include <memory>
#include "AsstDef.h"
@@ -22,9 +21,6 @@ namespace asst {
bool set_param(const std::string& type, const std::string& param, const std::string& value);
Configer& operator=(const Configer& rhs) = default;
Configer& operator=(Configer&& rhs) noexcept = default;
constexpr static int DefaultWindowWidth = 1280;
constexpr static int DefaultWindowHeight = 720;
constexpr static double Defaulttempl_threshold = 0.9;
@@ -42,6 +38,9 @@ namespace asst {
Configer(const Configer& rhs) = default;
Configer(Configer&& rhs) noexcept = default;
Configer& operator=(const Configer& rhs) = default;
Configer& operator=(Configer && rhs) noexcept = default;
bool _load(const std::string& filename);
};
}

View File

@@ -29,7 +29,7 @@ namespace asst {
std::tuple<AlgorithmType, double, asst::Rect> find_image(const cv::Mat& image, const std::string& templ, double templ_threshold);
// for debug
void feature_matching(const cv::Mat& mat);
std::vector<TextArea> feature_matching(const cv::Mat& mat);
void clear_cache();

View File

@@ -0,0 +1,36 @@
#pragma once
#include <string>
#include <unordered_map>
#include <unordered_set>
#include "AsstDef.h"
namespace asst {
class InfrastConfiger
{
public:
~InfrastConfiger() = default;
static InfrastConfiger& get_instance() {
static InfrastConfiger unique_instance;
return unique_instance;
}
bool load(const std::string& filename);
std::vector<std::vector<std::string>> m_mfg_combs; // 制造站组合
std::unordered_set<std::string> m_mfg_opers; // 制造站所有干员
std::string m_mfg_end; // 识别到这个词,就认为干员遍历结束,一般用排序里最后的那个干员(?)
private:
InfrastConfiger() = default;
InfrastConfiger(const InfrastConfiger& rhs) = default;
InfrastConfiger(InfrastConfiger&& rhs) noexcept = default;
InfrastConfiger& operator=(const InfrastConfiger& rhs) = default;
InfrastConfiger& operator=(InfrastConfiger && rhs) noexcept = default;
bool _load(const std::string& filename);
};
}

View File

@@ -1,39 +0,0 @@
#pragma once
#include <string>
#include <vector>
#include <unordered_set>
#include "AsstDef.h"
namespace asst {
class OpenRecruitConfiger
{
public:
~OpenRecruitConfiger() = default;
static OpenRecruitConfiger& get_instance() {
static OpenRecruitConfiger unique_instance;
return unique_instance;
}
bool load(const std::string& filename);
OpenRecruitConfiger& operator=(const OpenRecruitConfiger& rhs) = default;
OpenRecruitConfiger& operator=(OpenRecruitConfiger&& rhs) noexcept = default;
std::unordered_set<std::string> m_all_tags;
std::unordered_set<std::string> m_all_types;
std::vector<OperInfo> m_all_opers;
constexpr static int CorrectNumberOfTags = 5;
private:
OpenRecruitConfiger() = default;
OpenRecruitConfiger(const OpenRecruitConfiger& rhs) = default;
OpenRecruitConfiger(OpenRecruitConfiger&& rhs) noexcept = default;
bool _load(const std::string& filename);
};
}

View File

@@ -0,0 +1,39 @@
#pragma once
#include <string>
#include <vector>
#include <unordered_set>
#include "AsstDef.h"
namespace asst {
class RecruitConfiger
{
public:
~RecruitConfiger() = default;
static RecruitConfiger& get_instance() {
static RecruitConfiger unique_instance;
return unique_instance;
}
bool load(const std::string& filename);
std::unordered_set<std::string> m_all_tags;
std::unordered_set<std::string> m_all_types;
std::vector<OperInfo> m_all_opers;
constexpr static int CorrectNumberOfTags = 5;
private:
RecruitConfiger() = default;
RecruitConfiger(const RecruitConfiger& rhs) = default;
RecruitConfiger(RecruitConfiger&& rhs) noexcept = default;
RecruitConfiger& operator=(const RecruitConfiger& rhs) = default;
RecruitConfiger& operator=(RecruitConfiger && rhs) noexcept = default;
bool _load(const std::string& filename);
};
}

View File

@@ -25,7 +25,7 @@ namespace asst {
class WinMacro;
class Identify;
class Configer;
class OpenRecruitConfiger;
class RecruitConfiger;
enum TaskType {
TaskTypeInvalid = 0,
@@ -59,8 +59,25 @@ namespace asst {
RecruitTagsDetected,
OcrResultError,
RecruitSpecialTag,
RecruitResult
RecruitResult,
/* Infrast Msg*/
InfrastOpers, // 识别到基建干员s
InfrastComb // 当前设置的最优干员组合
};
// 设施类型
enum class FacilityType {
Invalid,
Manufacturing, // 制造站
Trade, // 贸易站
PowerStation, // 发电站
ControlInterface, // 控制中枢
ReceptionRoom, // 会客室
Dormitory, // 宿舍
Office // 办公室
// 训练室和加工站用不上,不加了
};
static std::ostream& operator<<(std::ostream& os, const AsstMsg& type)
{
static const std::unordered_map<AsstMsg, std::string> _type_name = {
@@ -85,7 +102,10 @@ namespace asst {
{AsstMsg::OcrResultError, "OcrResultError"},
{AsstMsg::RecruitSpecialTag, "RecruitSpecialTag"},
{AsstMsg::RecruitResult, "RecruitResult"},
{AsstMsg::AppendTask, "AppendTask"}
{AsstMsg::AppendTask, "AppendTask"},
{AsstMsg::InfrastOpers, "InfrastOpers"},
{AsstMsg::InfrastComb, "InfrastComb"}
};
return os << _type_name.at(type);
}
@@ -121,7 +141,7 @@ namespace asst {
protected:
virtual cv::Mat get_format_image();
virtual bool set_control_scale(int cur_width, int cur_height);
virtual void sleep(unsigned millisecond);
virtual bool sleep(unsigned millisecond);
virtual bool print_window(const std::string& dir);
std::shared_ptr<WinMacro> m_window_ptr = nullptr;
@@ -236,6 +256,33 @@ namespace asst {
bool m_set_time = false;
};
// 基建进驻任务
class InfrastStationTask : public OcrAbstractTask
{
public:
InfrastStationTask(AsstCallback callback, void* callback_arg);
virtual ~InfrastStationTask() = default;
virtual bool run() override;
void set_swipe_param(int delay, const Rect& begin_rect, const Rect& end_rect, int max_times = 20)
{
m_swipe_delay = delay;
m_swipe_begin = begin_rect;
m_swipe_end = end_rect;
m_swipe_max_times = max_times;
}
void set_facility(FacilityType facility)
{
m_facility = facility;
}
private:
FacilityType m_facility = FacilityType::Invalid;
int m_swipe_delay = 0;
Rect m_swipe_begin;
Rect m_swipe_end;
int m_swipe_max_times = 0;
};
// for debug
class TestOcrTask : public OcrAbstractTask
{

View File

@@ -25,6 +25,8 @@ namespace asst {
bool hideWindow();
bool click(const Point& p);
bool click(const Rect& rect);
bool swipe(const Point& p1, const Point& p2);
bool swipe(const Rect& r1, const Rect& r2);
void setControlScale(double scale);
cv::Mat getImage(const Rect& rect);
Rect getWindowRect();
@@ -35,12 +37,14 @@ namespace asst {
private:
bool findHandle();
std::optional<std::string> callCmd(const std::string& cmd, bool use_pipe = true);
Point randPointInRect(const Rect& rect);
const EmulatorInfo m_emulator_info;
const HandleType m_handle_type;
HWND m_handle = NULL;
bool m_is_adb = false;
std::string m_click_cmd; // adb点击命令不是adb的句柄用不到这个
std::string m_swipe_cmd; // adb滑动命令不是adb的句柄用不到这个
std::minstd_rand m_rand_engine;
int m_width = 0;
int m_height = 0;

View File

@@ -6,7 +6,8 @@
#include "Logger.hpp"
#include "AsstAux.h"
#include "Task.h"
#include "OpenRecruitConfiger.h"
#include "RecruitConfiger.h"
#include "InfrastConfiger.h"
#include <json.h>
#include <opencv2/opencv.hpp>
@@ -33,7 +34,12 @@ Assistance::Assistance(AsstCallback callback, void* callback_arg)
callback_error();
return;
}
ret = OpenRecruitConfiger::get_instance().load(GetResourceDir() + "operInfo.json");
ret = RecruitConfiger::get_instance().load(GetResourceDir() + "recruit.json");
if (!ret) {
callback_error();
return;
}
ret = InfrastConfiger::get_instance().load(GetResourceDir() + "infrast.json");
if (!ret) {
callback_error();
return;
@@ -62,6 +68,9 @@ Assistance::Assistance(AsstCallback callback, void* callback_arg)
return;
}
// for debug
m_identify_ptr->add_text_image("森蚺", GetResourceDir() + "operators\\森蚺.png");
m_working_thread = std::thread(working_proc, this);
m_msg_thread = std::thread(msg_proc, this);
}
@@ -214,6 +223,29 @@ bool Assistance::start_open_recruit(const std::vector<int>& required_level, bool
return true;
}
bool asst::Assistance::start_infrast()
{
DebugTraceFunction;
if (!m_thread_idle || !m_inited)
{
return false;
}
std::unique_lock<std::mutex> lock(m_mutex);
auto task_ptr = std::make_shared<InfrastStationTask>(task_callback, (void*)this);
// TODO 这个参数写到配置文件里
task_ptr->set_swipe_param(2000, Rect(600, 300, 0, 0), Rect(450, 300, 0, 0), 20);
task_ptr->set_facility(FacilityType::Manufacturing);
task_ptr->set_task_chain("Infrast");
m_tasks_queue.emplace(task_ptr);
m_thread_idle = false;
m_condvar.notify_one();
return true;
}
void Assistance::stop(bool block)
{
DebugTraceFunction;
@@ -240,6 +272,11 @@ bool Assistance::set_param(const std::string& type, const std::string& param, co
return Configer::get_instance().set_param(type, param, value);
}
bool asst::Assistance::swipe(const Point& p1, const Point& p2)
{
return m_control_ptr->swipe(p1, p2);
}
void Assistance::working_proc(Assistance* p_this)
{
DebugTraceFunction;

View File

@@ -112,6 +112,15 @@ bool AsstRunOpenRecruit(asst::Assistance* p_asst, const int required_level[], bo
return true;
}
bool AsstStartInfrast(asst::Assistance* p_asst)
{
if (p_asst == NULL) {
return false;
}
p_asst->start_infrast();
return true;
}
bool AsstTestOcr(asst::Assistance* p_asst, const char** text_array, int array_size, bool need_click)
{
if (p_asst == NULL || text_array == nullptr) {
@@ -140,4 +149,12 @@ bool CheckVersionUpdate(char* tag_buffer, int tag_bufsize, char* html_url_buffer
strcpy_s(html_url_buffer, html_bufsize, info.html_url.c_str());
strcpy_s(body_buffer, body_bufsize, info.body.c_str());
return true;
}
}
bool AsstTestSwipe(asst::Assistance* p_asst, int x1, int y1, int x2, int y2)
{
if (p_asst == NULL) {
return false;
}
return p_asst->swipe({ x1, y1 }, { x2, y2 });
}

View File

@@ -250,10 +250,10 @@ bool asst::Configer::_load(const std::string& filename)
}
if (emulator_json.exist("adb")) {
emulator_info.is_adb = true;
std::cout << emulator_json.to_string() << std::endl;
emulator_info.adb.path = emulator_json["adb"]["path"].as_string();
emulator_info.adb.connect = emulator_json["adb"]["connect"].as_string();
emulator_info.adb.click = emulator_json["adb"]["click"].as_string();
emulator_info.adb.swipe = emulator_json["adb"]["swipe"].as_string();
emulator_info.adb.display = emulator_json["adb"]["display"].as_string();
emulator_info.adb.display_regex = emulator_json["adb"]["displayRegex"].as_string();
}

View File

@@ -7,6 +7,8 @@
#include "Logger.hpp"
#include "AsstAux.h"
#include <algorithm>
using namespace asst;
using namespace cv;
using namespace cv::xfeatures2d;
@@ -80,7 +82,7 @@ std::pair<std::vector<cv::KeyPoint>, cv::Mat> asst::Identify::surf_detect(const
cv::Mat mat_gray;
cv::cvtColor(mat, mat_gray, cv::COLOR_RGB2GRAY);
constexpr int min_hessian = 800;
constexpr int min_hessian = 1000;
// SURF特征点检测
cv::Ptr<SURF> detector = SURF::create(min_hessian);
std::vector<KeyPoint> keypoints;
@@ -156,17 +158,64 @@ std::tuple<AlgorithmType, double, asst::Rect> Identify::find_image(const Mat& cu
}
}
void asst::Identify::feature_matching(const cv::Mat& mat)
std::vector<TextArea> asst::Identify::feature_matching(const cv::Mat& mat)
{
DebugTraceFunction;
auto && [income_keypoints, income_mat_vector] = surf_detect(mat);
static FlannBasedMatcher matcher;
std::vector<TextArea> matched_text_area;
for (auto&& [key, feature] : m_feature_map) {
auto&& [keypoints, mat_vector] = feature;
std::vector<cv::DMatch> matches;
matcher.match(mat_vector, income_mat_vector, matches);
// 最大的距离
auto max_iter = std::max_element(matches.cbegin(), matches.cend(),
[](const cv::DMatch& lhs, const cv::DMatch& rhs) ->bool{
return lhs.distance < rhs.distance;
}); // 描述符欧式距离knn
if (max_iter == matches.cend()) {
continue;
}
float maxdist = max_iter->distance;
// 最小的距离
auto min_iter = std::min_element(matches.cbegin(), matches.cend(),
[](const cv::DMatch& lhs, const cv::DMatch& rhs) ->bool {
return lhs.distance < rhs.distance;
}); // 描述符欧式距离knn
if (min_iter == matches.cend()) {
continue;
}
float mindist = min_iter->distance;
std::vector<cv::DMatch> good_matches;
constexpr static const double MatchRatio = 0.6;
for (const cv::DMatch dmatch : matches) {
if (dmatch.distance < maxdist * MatchRatio) {
good_matches.emplace_back(dmatch);
}
}
// TODO把这个阈值写到配置文件里
constexpr static const int MatchSizeThreshold = 10;
if (good_matches.size() >= MatchSizeThreshold) {
TextArea textarea;
textarea.text = key;
// TODO
//textarea.rect = ;
matched_text_area.emplace_back(std::move(textarea));
}
// for debug
cv::Mat text_mat = cv::imread(GetResourceDir() + "operators\\森蚺.png");
cv::Mat draw_mat;
cv::drawMatches(text_mat, keypoints, mat, income_keypoints, good_matches, draw_mat);
cv::imshow("output", draw_mat);
cv::waitKey(0);
}
return matched_text_area;
}
void Identify::clear_cache()

View File

@@ -1,4 +1,4 @@
#include "OpenRecruitConfiger.h"
#include "RecruitConfiger.h"
#include <fstream>
#include <sstream>
@@ -9,12 +9,12 @@
using namespace asst;
bool OpenRecruitConfiger::load(const std::string& filename)
bool RecruitConfiger::load(const std::string& filename)
{
DebugTraceFunction;
DebugTrace("OpenRecruitConfiger::load | ", filename);
DebugTrace("RecruitConfiger::load | ", filename);
OpenRecruitConfiger temp;
RecruitConfiger temp;
if (temp._load(filename)) {
// 按干员等级排个序
std::sort(temp.m_all_opers.begin(), temp.m_all_opers.end(), [](
@@ -31,7 +31,7 @@ bool OpenRecruitConfiger::load(const std::string& filename)
}
}
bool asst::OpenRecruitConfiger::_load(const std::string& filename)
bool asst::RecruitConfiger::_load(const std::string& filename)
{
std::ifstream ifs(filename, std::ios::in);
if (!ifs.is_open()) {

View File

@@ -3,7 +3,8 @@
#include "WinMacro.h"
#include "Identify.h"
#include "Configer.h"
#include "OpenRecruitConfiger.h"
#include "RecruitConfiger.h"
#include "InfrastConfiger.h"
#include <json.h>
#include "AsstAux.h"
@@ -13,6 +14,7 @@
#include <cmath>
#include <mutex>
#include <filesystem>
#include <future>
using namespace asst;
@@ -80,10 +82,10 @@ bool AbstractTask::set_control_scale(int cur_width, int cur_height)
return true;
}
void AbstractTask::sleep(unsigned millisecond)
bool AbstractTask::sleep(unsigned millisecond)
{
if (millisecond == 0) {
return;
return true;
}
auto start = std::chrono::system_clock::now();
unsigned duration = 0;
@@ -99,6 +101,8 @@ void AbstractTask::sleep(unsigned millisecond)
std::this_thread::yield();
}
m_callback(AsstMsg::EndOfSleep, callback_json, m_callback_arg);
return (m_exit_flag == NULL || *m_exit_flag == false);
}
bool AbstractTask::print_window(const std::string& dir)
@@ -180,7 +184,10 @@ bool ProcessTask::run()
}
// 前置固定延时
sleep(task_info_ptr->pre_delay);
if (!sleep(task_info_ptr->pre_delay))
{
return false;
}
bool need_stop = false;
switch (task_info_ptr->type) {
@@ -198,7 +205,10 @@ bool ProcessTask::run()
break;
case ProcessTaskType::PrintWindow:
{
sleep(Configer::get_instance().m_options.print_window_delay);
if (!sleep(Configer::get_instance().m_options.print_window_delay))
{
return false;
}
static const std::string dirname = GetCurrentDir() + "screenshot\\";
print_window(dirname);
}
@@ -224,7 +234,9 @@ bool ProcessTask::run()
m_callback(AsstMsg::TaskCompleted, callback_json, m_callback_arg);
// 后置固定延时
sleep(task_info_ptr->rear_delay);
if (!sleep(task_info_ptr->rear_delay)) {
return false;
}
json::value next_json = callback_json;
next_json["task_chain"] = m_task_chain;
@@ -367,7 +379,9 @@ void ProcessTask::exec_click_task(const Rect& matched_rect)
Configer::get_instance().m_options.control_delay_upper);
unsigned rand_delay = rand_uni(rand_engine);
sleep(rand_delay);
if (!sleep(rand_delay)) {
return;
}
}
m_control_ptr->click(matched_rect);
@@ -428,7 +442,7 @@ bool OpenRecruitTask::run()
/* Filter out all tags from all text */
std::vector<TextArea> all_tags = text_match(
all_text_area, OpenRecruitConfiger::get_instance().m_all_tags, Configer::get_instance().m_recruit_ocr_replace);
all_text_area, RecruitConfiger::get_instance().m_all_tags, Configer::get_instance().m_recruit_ocr_replace);
std::unordered_set<std::string> all_tags_name;
std::vector<json::value> all_tags_json_vector;
@@ -441,7 +455,7 @@ bool OpenRecruitTask::run()
m_callback(AsstMsg::RecruitTagsDetected, all_tags_json, m_callback_arg);
/* 过滤tags数量不足的情况可能是识别漏了 */
if (all_tags.size() != OpenRecruitConfiger::CorrectNumberOfTags) {
if (all_tags.size() != RecruitConfiger::CorrectNumberOfTags) {
all_tags_json["type"] = "OpenRecruit";
m_callback(AsstMsg::OcrResultError, all_tags_json, m_callback_arg);
return false;
@@ -488,7 +502,7 @@ bool OpenRecruitTask::run()
// 例如 key: { "狙击"、"群攻" }value: OperCombs.opers{ "陨星", "白雪", "空爆" }
std::map<std::vector<std::string>, OperCombs> result_map;
for (const std::vector<std::string>& comb : all_combs) {
for (const OperInfo& cur_oper : OpenRecruitConfiger::get_instance().m_all_opers) {
for (const OperInfo& cur_oper : RecruitConfiger::get_instance().m_all_opers) {
int matched_count = 0;
for (const std::string& tag : comb) {
if (cur_oper.tags.find(tag) != cur_oper.tags.cend()) {
@@ -659,39 +673,268 @@ bool TestOcrTask::run()
};
m_callback(AsstMsg::TaskStart, task_start_json, m_callback_arg);
/* Find all text */
std::vector<TextArea> all_text_area = ocr_detect();
auto swipe_foo = [&](bool reverse = false) -> bool {
const Point p1(600, 300);
const Point p2(450, 300);
bool ret = false;
if (!reverse) {
ret = m_control_ptr->swipe(p1, p2);
}
else {
ret = m_control_ptr->swipe(p2, p1);
}
ret &= sleep(3000);
return ret;
};
std::vector<json::value> all_text_json_vector;
for (const TextArea& text_area : all_text_area) {
all_text_json_vector.emplace_back(Utf8ToGbk(text_area.text));
}
json::value all_text_json;
all_text_json["text"] = json::array(all_text_json_vector);
m_callback(AsstMsg::TextDetected, all_text_json, m_callback_arg);
// 识别到的干员名
std::unordered_set<std::string> all_names;
/* Filter out all text from all text */
std::vector<TextArea> all_text = text_search(
all_text_area, m_text_vec, Configer::get_instance().m_infrast_ocr_replace);
std::vector<json::value> all_tags_json_vector;
for (const TextArea& text_area : all_text) {
all_tags_json_vector.emplace_back(Utf8ToGbk(text_area.text));
}
json::value all_tags_json;
all_tags_json["tags"] = json::array(all_tags_json_vector);
m_callback(AsstMsg::RecruitTagsDetected, all_tags_json, m_callback_arg);
// 一边识别一边滑动,把所有制造站干员名字抓出来
for (int i = 0; i != 20; ++i) {
const cv::Mat& image = get_format_image();
// 异步进行滑动操作
std::future<bool> swipe_future = std::async(std::launch::async, swipe_foo);
// 点击识别到的文字,直接回调扔出去,交给外层做
if (m_need_click) {
for (const TextArea& text_area : all_text) {
json::value task_json;
task_json["type"] = "ClickTask";
task_json["rect"] = json::array({ text_area.rect.x, text_area.rect.y, text_area.rect.width, text_area.rect.height });
task_json["retry_times"] = m_retry_times;
task_json["task_chain"] = m_task_chain;
m_callback(AsstMsg::AppendTask, task_json, m_callback_arg);
std::vector<TextArea> all_text_area = ocr_detect(image);
/* 过滤出所有制造站中的干员名 */
std::vector<TextArea> cur_names = text_search(
all_text_area,
InfrastConfiger::get_instance().m_mfg_opers,
Configer::get_instance().m_infrast_ocr_replace);
for (TextArea& text_area : cur_names) {
all_names.emplace(std::move(text_area.text));
}
bool break_flag = false;
// 识别到了结束标记,就直接退出循环
if (all_names.find(InfrastConfiger::get_instance().m_mfg_end) != all_names.cend()) {
break_flag = true;
}
// 阻塞等待滑动结束
if (!swipe_future.get()) {
return false;
}
if (break_flag) {
break;
}
}
std::cout << "识别到制造站干员:" << std::endl;
for (const std::string& name : all_names) {
std::cout << Utf8ToGbk(name) << std::endl;
}
// 配置文件中的干员组合,和抓出来的干员名比对,如果组合中的干员都有,那就用这个组合
// Todo 时间复杂度起飞了,需要优化下
std::vector<std::string> optimal_comb;
for (auto&& name_vec : InfrastConfiger::get_instance().m_mfg_combs) {
int count = 0;
for (std::string& name : name_vec) {
if (all_names.find(name) != all_names.cend()) {
++count;
}
else {
break;
}
}
if (count == name_vec.size()) {
optimal_comb = name_vec;
break;
}
}
std::cout << "最优组合:" << std::endl;
for (const std::string& name : optimal_comb) {
std::cout << Utf8ToGbk(name) << std::endl;
}
// 一边滑动一边点击最优解中的干员
for (int i = 0; i != 20; ++i) {
const cv::Mat& image = get_format_image();
std::vector<TextArea> all_text_area = ocr_detect(image);
/* 过滤出所有制造站中的干员名 */
std::vector<TextArea> cur_names = text_search(
all_text_area,
optimal_comb,
Configer::get_instance().m_infrast_ocr_replace);
for (TextArea& text_area : cur_names) {
m_control_ptr->click(text_area.rect);
// 点过了就不会再点了直接从最优解vector里面删了
auto iter = std::find(optimal_comb.begin(), optimal_comb.end(), text_area.text);
if (iter != optimal_comb.end()) {
optimal_comb.erase(iter);
}
}
if (optimal_comb.empty()) {
break;
}
// 因为滑动和点击是矛盾的,这里没法异步做
if (!swipe_foo(true)) {
return false;
}
}
//// 点击识别到的文字,直接回调扔出去,交给外层做
//if (m_need_click) {
// for (const TextArea& text_area : all_text) {
// json::value task_json;
// task_json["type"] = "ClickTask";
// task_json["rect"] = json::array({ text_area.rect.x, text_area.rect.y, text_area.rect.width, text_area.rect.height });
// task_json["retry_times"] = m_retry_times;
// task_json["task_chain"] = m_task_chain;
// m_callback(AsstMsg::AppendTask, task_json, m_callback_arg);
// }
//}
return true;
}
}
asst::InfrastStationTask::InfrastStationTask(AsstCallback callback, void* callback_arg)
: OcrAbstractTask(callback, callback_arg)
{
}
bool asst::InfrastStationTask::run()
{
if (m_view_ptr == NULL
|| m_identify_ptr == NULL)
{
m_callback(AsstMsg::PtrIsNull, json::value(), m_callback_arg);
return false;
}
// for debug
const cv::Mat& debug_image = get_format_image();
m_identify_ptr->feature_matching(debug_image);
std::vector<std::vector<std::string>> all_oper_combs; // 所有的干员组合
std::unordered_set<std::string> all_oper_name; // 所有干员名
std::string oper_end_flag; // 干员名结束标记识别到这个string就认为识别完成了
switch (m_facility) {
case FacilityType::Manufacturing:
all_oper_combs = InfrastConfiger::get_instance().m_mfg_combs;
all_oper_name = InfrastConfiger::get_instance().m_mfg_opers;
oper_end_flag = InfrastConfiger::get_instance().m_mfg_end;
break;
// TODO 贸易站和其他啥的,有空再做
default:
break;
}
json::value task_start_json = json::object{
{ "task_type", "InfrastStationTask" },
{ "task_chain", m_task_chain},
};
m_callback(AsstMsg::TaskStart, task_start_json, m_callback_arg);
auto swipe_foo = [&](bool reverse = false) -> bool {
bool ret = false;
if (!reverse) {
ret = m_control_ptr->swipe(m_swipe_begin, m_swipe_end);
}
else {
ret = m_control_ptr->swipe(m_swipe_end, m_swipe_begin);
}
ret &= sleep(m_swipe_delay);
return ret;
};
// 识别到的干员名
std::unordered_set<std::string> detected_names;
// 一边识别一边滑动,把所有制造站干员名字抓出来
for (int i = 0; i != m_swipe_max_times; ++i) {
const cv::Mat& image = get_format_image();
// 异步进行滑动操作
std::future<bool> swipe_future = std::async(std::launch::async, swipe_foo);
std::vector<TextArea> all_text_area = ocr_detect(image);
/* 过滤出所有制造站中的干员名 */
std::vector<TextArea> cur_names = text_search(
all_text_area,
all_oper_name,
Configer::get_instance().m_infrast_ocr_replace);
for (TextArea& text_area : cur_names) {
detected_names.emplace(std::move(text_area.text));
}
bool break_flag = false;
// 识别到了结束标记,就直接退出循环
if (detected_names.find(oper_end_flag) != detected_names.cend()) {
break_flag = true;
}
// 阻塞等待滑动结束
if (!swipe_future.get()) {
return false;
}
if (break_flag) {
break;
}
}
json::value opers_json;
std::vector<json::value> all_names_vector;
for (const std::string& name : detected_names) {
all_names_vector.emplace_back(Utf8ToGbk(name));
}
opers_json["names"] = json::array(all_names_vector);
m_callback(AsstMsg::InfrastOpers, opers_json, m_callback_arg);
// 配置文件中的干员组合,和抓出来的干员名比对,如果组合中的干员都有,那就用这个组合
// Todo 时间复杂度起飞了,需要优化下
std::vector<std::string> optimal_comb;
for (auto&& name_vec :all_oper_combs) {
int count = 0;
for (std::string& name : name_vec) {
if (detected_names.find(name) != detected_names.cend()) {
++count;
}
else {
break;
}
}
if (count == name_vec.size()) {
optimal_comb = name_vec;
break;
}
}
opers_json["comb"] = json::array(optimal_comb);
m_callback(AsstMsg::InfrastComb, opers_json, m_callback_arg);
// 一边滑动一边点击最优解中的干员
for (int i = 0; i != m_swipe_max_times; ++i) {
const cv::Mat& image = get_format_image();
std::vector<TextArea> all_text_area = ocr_detect(image);
/* 过滤出所有制造站中的干员名 */
std::vector<TextArea> cur_names = text_search(
all_text_area,
optimal_comb,
Configer::get_instance().m_infrast_ocr_replace);
for (TextArea& text_area : cur_names) {
m_control_ptr->click(text_area.rect);
// 点过了就不会再点了直接从最优解vector里面删了
auto iter = std::find(optimal_comb.begin(), optimal_comb.end(), text_area.text);
if (iter != optimal_comb.end()) {
optimal_comb.erase(iter);
}
}
if (optimal_comb.empty()) {
break;
}
// 因为滑动和点击是矛盾的,这里没法异步做
if (!swipe_foo(true)) {
return false;
}
}
return true;
}

View File

@@ -114,6 +114,7 @@ bool WinMacro::findHandle()
}
m_click_cmd = adb_dir + m_emulator_info.adb.click;
m_swipe_cmd = adb_dir + m_emulator_info.adb.swipe;
}
DebugTrace("Handle:", m_handle, "Name:", m_emulator_info.name, "Type:", m_handle_type);
@@ -190,6 +191,29 @@ std::optional<std::string> asst::WinMacro::callCmd(const std::string& cmd, bool
return pipe_str;
}
Point asst::WinMacro::randPointInRect(const Rect& rect)
{
int x = 0, y = 0;
if (rect.width == 0) {
x = rect.x;
}
else {
int x_rand = std::poisson_distribution<int>(rect.width / 2)(m_rand_engine);
x = x_rand + rect.x;
}
if (rect.height == 0) {
y = rect.y;
}
else {
int y_rand = std::poisson_distribution<int>(rect.height / 2)(m_rand_engine);
y = y_rand + rect.y;
}
return Point(x, y);
}
bool WinMacro::resizeWindow(int width, int height)
{
if (m_handle_type != HandleType::Window || !::IsWindow(m_handle)) {
@@ -289,25 +313,41 @@ bool WinMacro::click(const Rect& rect)
return false;
}
int x = 0, y = 0;
if (rect.width == 0) {
x = rect.x;
}
else {
int x_rand = std::poisson_distribution<int>(rect.width / 2)(m_rand_engine);
return click(randPointInRect(rect));
}
x = x_rand + rect.x;
bool asst::WinMacro::swipe(const Point& p1, const Point& p2)
{
if (m_handle_type != HandleType::Control || !::IsWindow(m_handle)) {
return false;
}
if (rect.height == 0) {
y = rect.y;
int x1 = p1.x * m_control_scale;
int y1 = p1.y * m_control_scale;
int x2 = p2.x * m_control_scale;
int y2 = p2.y * m_control_scale;
DebugTrace("Swipe, raw:", p1.x, p1.y, p2.x, p2.y, "corr:", x1, y1, x2, y2);
if (m_is_adb) {
std::string cur_cmd = StringReplaceAll(m_swipe_cmd, "[x1]", std::to_string(x1));
cur_cmd = StringReplaceAll(cur_cmd, "[y1]", std::to_string(y1));
cur_cmd = StringReplaceAll(cur_cmd, "[x2]", std::to_string(x2));
cur_cmd = StringReplaceAll(cur_cmd, "[y2]", std::to_string(y2));
return callCmd(cur_cmd, false).has_value();
return true;
}
else {
int y_rand = std::poisson_distribution<int>(rect.height / 2)(m_rand_engine);
y = y_rand + rect.y;
else { // TODO
return false;
}
}
bool asst::WinMacro::swipe(const Rect& r1, const Rect& r2)
{
if (m_handle_type != HandleType::Control || !::IsWindow(m_handle)) {
return false;
}
return click({ x, y });
return swipe(randPointInRect(r1), randPointInRect(r2));
}
void asst::WinMacro::setControlScale(double scale)

View File

@@ -123,7 +123,7 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x64'">
<LinkIncremental>true</LinkIncremental>
<IncludePath>$(SolutionDir)meojson\include;$(SolutionDir)MeoAssistance\include;$(IncludePath)</IncludePath>
<IncludePath>$(SolutionDir)MeoAssistance\include;$(IncludePath)</IncludePath>
<LibraryPath>$(TargetDir);$(LibraryPath)</LibraryPath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">

View File

@@ -1,9 +1,14 @@
#include "AsstCaller.h"
#include <stdio.h>
using namespace asst;
void test_dorm(Assistance* ptr);
void test_swipe(Assistance* ptr);
void test_infrast(Assistance* ptr);
int main(int argc, char** argv)
{
using namespace asst;
Assistance* ptr = AsstCreate();
auto ret = AsstCatchEmulator(ptr);
@@ -15,11 +20,13 @@ int main(int argc, char** argv)
}
return -1;
}
const char* text_array[] = { "×¢ÒâÁ¦" };
char ch = 0;
while (ch != 'q') {
AsstTestOcr(ptr, text_array, sizeof(text_array) / sizeof(char*), true);
test_infrast(ptr);
//test_swipe(ptr);
ch = getchar();
}
@@ -29,4 +36,21 @@ int main(int argc, char** argv)
}
return 0;
}
void test_swipe(Assistance* ptr)
{
AsstTestSwipe(ptr, 1000, 300, 500, 300);
}
void test_dorm(Assistance* ptr)
{
const char* text_array[] = { "×¢ÒâÁ¦" };
AsstTestOcr(ptr, text_array, sizeof(text_array) / sizeof(char*), true);
}
void test_infrast(Assistance* ptr)
{
AsstStartInfrast(ptr);
}

View File

@@ -30,6 +30,9 @@
[ "都任", "输出" ]
],
"infrastOcrReplace": [
[ "森蛐", "森蚺" ],
[ "森蛾", "森蚺" ],
[ "森躺", "森蚺" ]
],
"handle_Doc": "下面的和模拟器窗口捕获逻辑有关,不需要修改",
"handle": {
@@ -60,115 +63,19 @@
"window": "BlueStacks Android PluginAndroid"
}
],
"adb": {
"path": "[EmulatorPath]HD-Adb.exe",
"connect": " connect 127.0.0.1:5555",
"click": " -s 127.0.0.1:5555 shell input tap [x] [y]",
"swipe": " -s 127.0.0.1:5555 shell input swipe [x1] [y1] [x2] [y2]",
"display": " -s 127.0.0.1:5555 shell dumpsys window displays | grep init= | awk ' { print $3 } '",
"displayRegex": "cur=%dx%d"
},
"xOffset": 11,
"yOffset": 46,
"rightOffset": 11,
"bottomOffset": 10
},
"Nox": {
"window": [
{
"class": "Qt5QWindowIcon",
"window": "夜神模拟器"
}
],
"view": [
{
"class": "Qt5QWindowIcon",
"window": "夜神模拟器"
}
],
"control": [
{
"class": "Qt5QWindowIcon",
"window": "夜神模拟器"
}
],
"xOffset": 2,
"yOffset": 39,
"rightOffset": 39,
"bottomOffset": 9
},
"LDPlayer": {
"window": [
{
"class": "LDPlayerMainFrame",
"window": "雷电模拟器"
}
],
"view": [
{
"class": "LDPlayerMainFrame",
"window": "雷电模拟器"
}
],
"control": [
{
"class": "LDPlayerMainFrame",
"window": "雷电模拟器"
},
{
"class": "RenderWindow",
"window": "TheRender"
}
],
"xOffset": 0,
"yOffset": 35
},
"XYAZ": {
"window": [
{
"class": "Qt5QWindowIcon",
"window": "逍遥模拟器"
}
],
"view": [
{
"class": "Qt5QWindowIcon",
"window": "逍遥模拟器"
}
],
"control": [
{
"class": "Qt5QWindowIcon",
"window": "逍遥模拟器"
},
{
"class": "Qt5QWindowIcon",
"window": "MainWindowWindow"
}
],
"xOffset": 1,
"yOffset": 48,
"rightOffset": 1,
"bottomOffset": 1
},
"Tencent": {
"window": [
{
"class": "TXGuiFoundation",
"window": "腾讯手游助手"
}
],
"view": [
{
"class": "TXGuiFoundation",
"window": "腾讯手游助手"
}
],
"control": [
{
"class": "TXGuiFoundation",
"window": "腾讯手游助手"
},
{
"class": "AEngineRenderWindowClass",
"window": "AEngineRenderWindow"
}
],
"xOffset": 1,
"yOffset": 42
},
"MuMuEmulator": {
"window": [
{
@@ -191,8 +98,9 @@
"adb": {
"path": "[EmulatorPath]..\\\\vmonitor\\\\bin\\\\adb_server.exe",
"connect": " connect 127.0.0.1:7555",
"click": " shell input tap [x] [y]",
"display": " shell dumpsys window displays | grep init= | awk ' { print $3 } '",
"click": " -s 127.0.0.1:7555 shell input tap [x] [y]",
"swipe": " -s 127.0.0.1:7555 shell input swipe [x1] [y1] [x2] [y2]",
"display": " -s 127.0.0.1:7555 shell dumpsys window displays | grep init= | awk ' { print $3 } '",
"displayRegex": "cur=%dx%d"
},
"xOffset": 0,

34
resource/infrast.json Normal file
View File

@@ -0,0 +1,34 @@
{
"Manufacturing": {
"Doc": "制造站",
"combs": [
{
"comb": [
"刻俄柏",
"稀音",
"红云"
],
"efficiency": 105
},
{
"comb": [
"温蒂",
"异客",
"森蚺"
],
"efficiency": 90
},
{
"comb": [
"月见夜"
],
"efficiency": 10
}
],
"end": "月见夜",
"end_Doc": "识别到这个词,就认为干员遍历结束,一般用排序里最后的那个干员(?)"
},
"Trade": {
"Doc": "贸易站"
}
}

View File

@@ -0,0 +1,3 @@
adb connect 127.0.0.1:5555
adb -s 127.0.0.1:5555 shell screencap /sdcard/screen.png
adb -s 127.0.0.1:5555 pull /sdcard/screen.png .