Compare commits

..

65 Commits

Author SHA1 Message Date
MistEO
c8143501c1 Merge branch 'dev' of https://github.com/MistEO/MeoAssistance-Arknights into dev 2021-11-13 22:14:58 +08:00
MistEO
3c16fccca9 feat.进一步的异形屏流程任务适配,修复一些bug 2021-11-13 22:14:34 +08:00
MistEO
c79124f7c4 fix.再次修复贸易站无人机使用不完的问题 2021-11-12 10:53:20 +08:00
MistEO
d434d423e3 feat.优化对非16:9屏幕的支持 2021-11-11 23:31:13 +08:00
MistEO
f66c1e1c59 style.修改json为utf-8 2021-11-11 22:24:22 +08:00
MistEO
3b03f50a4a Merge branch 'master' of https://github.com/MistEO/MeoAssistance-Arknights 2021-11-11 22:18:40 +08:00
MistEO
0ca13ae16a fix.修复帕斯拉技能识别错误的问题 2021-11-11 22:17:41 +08:00
MistEO
ae020bda5d Merge branch 'dev' 2021-11-11 22:05:46 +08:00
MistEO
9cf5c351f3 chore.更新版本号 2021-11-11 22:05:29 +08:00
MistEO
574ec830a0 fix.修复贸易站无人机用不完的问题 2021-11-11 22:04:59 +08:00
MistEO
6e8e5e80d2 fix.修复清流效率计算错误的问题 2021-11-11 21:42:04 +08:00
MistEO
27c228e355 fix.修复两电站时的效率计算错误 2021-11-11 20:41:28 +08:00
MistEO
273c2b19ad fix.修复温和模式不清除原有干员的问题 2021-11-11 20:32:47 +08:00
MistEO
29d14f5a7b docs.Update README 2021-11-11 02:15:41 +08:00
MistEO
94cccfe424 docs.更新README 2021-11-11 01:18:27 +08:00
MistEO
ad1704df0d feat.更新基建界面 2021-11-11 00:39:34 +08:00
MistEO
bf6d929e80 feat.新增基建界面提示 2021-11-11 00:13:09 +08:00
MistEO
67036398bd fix.修复adb连接问题,优先连接addresses字段中的地址 2021-11-10 22:23:34 +08:00
MistEO
4a8c20c604 style.格式化所有json文件 2021-11-09 22:18:21 +08:00
MistEO
b6c6d46362 Revert "chore.升级项目配置至VS2022",由于appveyor不支持v143,暂时先回退了
This reverts commit 6bbc2c5615.
2021-11-09 22:12:06 +08:00
MistEO
c36c2c74ff fix.修复工具编译不过去的问题 2021-11-09 22:04:44 +08:00
MistEO
ed9e017ebb style.代码风格统一格式化 2021-11-09 22:04:03 +08:00
MistEO
762f701593 chore.更新editorconfig 2021-11-09 22:03:50 +08:00
Liam Sho
254f34a50d feat.添加 EditorConfig 和 ClangFormat 用于 C++ 项目
Visual Studio 对 C++ 项目的代码格式化支持好像不是特别的好,因此可能还是会有点问题
2021-11-09 21:30:19 +08:00
Liam Sho
9f5564522d feat.添加 EditorConfig 用于 C# 项目 2021-11-09 21:29:54 +08:00
MistEO
6bbc2c5615 chore.升级项目配置至VS2022 2021-11-09 21:14:19 +08:00
MistEO
34c50898d5 perf.优化ADB端口识别 2021-11-09 01:40:26 +08:00
MistEO
736244162c chore.暂时屏蔽偏激模式,修改版本号 2021-11-08 22:37:53 +08:00
MistEO
dbec3ae7ed feat.完成基建激进换班模式 2021-11-08 22:24:16 +08:00
MistEO
1395ec46b6 feat.打通基建工作模式设置相关接口 2021-11-08 21:09:59 +08:00
MistEO
27fc4a6bf6 chore.Update LICENSE 2021-11-07 23:46:24 +08:00
MistEO
33f7e677d7 chore.修改版本号,更新截图的脚本 2021-11-07 23:31:54 +08:00
MistEO
716d1971c9 Merge branch 'dev' 2021-11-07 23:30:32 +08:00
MistEO
bbaf8cdf80 fix.修复会客室线索空位识别问题 2021-11-05 20:55:34 +08:00
MistEO
09cb5b271a perf.添加一些日志,优化部分简单逻辑 2021-11-05 00:31:48 +08:00
MistEO
7f1eff2188 chore.完成自动打包脚本 2021-11-04 23:34:25 +08:00
MistEO
70a1428fa3 perf.优化基建停止按钮的响应速度 2021-11-04 22:43:35 +08:00
MistEO
0dd6524da9 chore.更新版本号v1.3-alpha7 2021-11-04 00:45:05 +08:00
MistEO
92c53f4a8b fix.帮雷电模拟器修复截图错误的bug 2021-11-04 00:44:36 +08:00
MistEO
51f51f4d3b fix.修复基建已选择干员的识别错误 2021-11-04 00:28:52 +08:00
MistEO
5722ca6191 perf.优化开启线索交流后的操作逻辑 2021-11-04 00:20:02 +08:00
MistEO
08d80e34b0 perf.优化已选择干员的识别算法,改用hsv计算蓝色像素点数量 2021-11-04 00:09:28 +08:00
MistEO
99f837ad7b fix.修复雷电、夜神模拟器连接问题 2021-11-03 22:30:57 +08:00
MistEO
35de8f5796 Merge branch 'dev' 2021-11-03 21:46:00 +08:00
MistEO
805b7d9d78 chore.删除一些错误添加到项目中的问题 2021-11-03 21:45:09 +08:00
MistEO
96f0d45680 Merge branch 'dev' 2021-11-03 10:06:27 +08:00
MistEO
32bfed1826 docs.更新招募广告( 2021-11-03 10:05:42 +08:00
MistEO
5b12678dcd chore.删除一些错误添加到项目中的问题 2021-11-03 10:04:06 +08:00
MistEO
7aec6eb49c chore.更新版本号 2021-11-02 23:44:59 +08:00
MistEO
c3fb83b241 fix.修复会客室线索空位识别不正确的问题 2021-11-02 23:13:00 +08:00
MistEO
808ae2307b fix.修复滑动延时错误的问题 2021-11-02 22:15:58 +08:00
MistEO
1bbc1b41cb chore.更新关卡掉落数据,更新版本号 2021-11-02 21:41:39 +08:00
MistEO
908cd9fe72 chore.更新关卡掉落数据,更新版本号 2021-11-02 15:23:11 +08:00
MistEO
7e935aaaea chore.更新关卡掉落数据,更新版本号 2021-11-02 15:21:30 +08:00
MistEO
c334b323aa fix.尝试修复贸易站无人机用不完的问题 2021-11-02 10:52:10 +08:00
MistEO
08230c2731 fix.修复发电站进不去的问题 2021-11-02 10:51:54 +08:00
MistEO
8d30393596 fix.修复滑动过程中乱选人的问题 2021-11-02 10:51:37 +08:00
MistEO
ab62f28a15 docs.基建换班模式,画饼(bushi 2021-11-02 00:10:20 +08:00
MistEO
7df3cdc8c8 perf.为滑动选择干员增加容错操作 2021-11-01 23:30:41 +08:00
MistEO
929a8fb1e1 feat.换班界面新增每个设施的开关 2021-11-01 22:49:58 +08:00
MistEO
ff91fc1d70 fix.修复公招关闭设置时间不生效的问题 2021-11-01 22:14:11 +08:00
MistEO
d6d0af43b4 chore.更新公招资源,更新版本号 2021-11-01 19:04:36 +08:00
MistEO
909d123f8f chore.更新公招资源,更新版本号 2021-11-01 18:44:13 +08:00
MistEO
10283bf72a chore.更新企鹅物流关卡json,11月1日活动 2021-11-01 16:24:11 +08:00
MistEO
c8a2fe3099 chore.更新企鹅物流关卡json,11月1日活动 2021-11-01 16:01:54 +08:00
136 changed files with 46910 additions and 2137 deletions

4
.gitignore vendored
View File

@@ -427,4 +427,6 @@ FodyWeavers.xsd
*.sln.iml
screen.png
adb_screen.png
adb_screen.png
tools/*.png
resource/infrast

View File

@@ -5,93 +5,93 @@
namespace json
{
static std::string unescape_string(std::string&& str)
{
std::string replace_str;
std::string escape_str = std::move(str);
static std::string unescape_string(std::string&& str)
{
std::string replace_str;
std::string escape_str = std::move(str);
for (size_t pos = 0; pos < escape_str.size(); ++pos)
{
switch (escape_str[pos]) {
case '\"':
replace_str = R"(\")";
break;
case '\\':
replace_str = R"(\\)";
break;
case '\b':
replace_str = R"(\b)";
break;
case '\f':
replace_str = R"(\f)";
break;
case '\n':
replace_str = R"(\n)";
break;
case '\r':
replace_str = R"(\r)";
break;
case '\t':
replace_str = R"(\t)";
break;
default:
continue;
break;
}
escape_str.replace(pos, 1, replace_str);
++pos;
}
return escape_str;
}
for (size_t pos = 0; pos < escape_str.size(); ++pos)
{
switch (escape_str[pos]) {
case '\"':
replace_str = R"(\")";
break;
case '\\':
replace_str = R"(\\)";
break;
case '\b':
replace_str = R"(\b)";
break;
case '\f':
replace_str = R"(\f)";
break;
case '\n':
replace_str = R"(\n)";
break;
case '\r':
replace_str = R"(\r)";
break;
case '\t':
replace_str = R"(\t)";
break;
default:
continue;
break;
}
escape_str.replace(pos, 1, replace_str);
++pos;
}
return escape_str;
}
static std::string unescape_string(const std::string& str)
{
return unescape_string(std::string(str));
}
static std::string unescape_string(const std::string& str)
{
return unescape_string(std::string(str));
}
static std::string escape_string(std::string&& str)
{
std::string escape_str = std::move(str);
static std::string escape_string(std::string&& str)
{
std::string escape_str = std::move(str);
for (size_t pos = 0; pos + 1 < escape_str.size(); ++pos)
{
if (escape_str[pos] != '\\') {
continue;
}
std::string replace_str;
switch (escape_str[pos+1]) {
case '"':
replace_str = "\"";
break;
case '\\':
replace_str = "\\";
break;
case 'b':
replace_str = "\b";
break;
case 'f':
replace_str = "\f";
break;
case 'n':
replace_str = "\n";
break;
case 'r':
replace_str = "\r";
break;
case 't':
replace_str = "\r";
break;
default:
return std::string();
break;
}
escape_str.replace(pos, 2, replace_str);
}
return escape_str;
}
for (size_t pos = 0; pos + 1 < escape_str.size(); ++pos)
{
if (escape_str[pos] != '\\') {
continue;
}
std::string replace_str;
switch (escape_str[pos + 1]) {
case '"':
replace_str = "\"";
break;
case '\\':
replace_str = "\\";
break;
case 'b':
replace_str = "\b";
break;
case 'f':
replace_str = "\f";
break;
case 'n':
replace_str = "\n";
break;
case 'r':
replace_str = "\r";
break;
case 't':
replace_str = "\t";
break;
default:
return std::string();
break;
}
escape_str.replace(pos, 2, replace_str);
}
return escape_str;
}
static std::string escape_string(const std::string& str)
{
return escape_string(std::string(str));
}
static std::string escape_string(const std::string& str)
{
return escape_string(std::string(str));
}
}

Binary file not shown.

File diff suppressed because one or more lines are too long

143
LICENSE
View File

@@ -1,5 +1,5 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
@@ -7,17 +7,15 @@
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
@@ -26,44 +24,34 @@ them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
@@ -72,7 +60,7 @@ modification follow.
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
@@ -549,35 +537,45 @@ to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
@@ -635,40 +633,29 @@ the "copyright" line and a pointer to where the full notice is found.
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

View File

@@ -16,7 +16,7 @@
A game assistance for Arknights
一款明日方舟的游戏辅助供PC端安卓模拟器使用也勉强兼容安卓手机绝赞开发中……
一款明日方舟的游戏助手,自动刷理智、智能基建换班、公招识别,纯图像识别,全图形化界面,兼容模拟器和安卓设备,开罐即食,绝赞开发中!✿✿ヽ(°▽°)ノ✿
<br>
![界面截图](images/gui.png)
@@ -37,6 +37,7 @@ A game assistance for Arknights
- 单设施内最优解,会计算单设施内所有特殊干员组合
- 可识别赤金、经验书,分别使用不同的干员组合
- `迷迭香`等跨设施体系正在开发中!
- 详细换班功能介绍见[基建换班说明](#基建换班)
- 自动刷理智
- 支持刷完自动上传[企鹅物流数据统计](https://penguin-stats.cn/)
- 界面支持统计掉落数量
@@ -57,7 +58,7 @@ A game assistance for Arknights
- 自动帮你点击时间9小时
- 出5、6星干员弹窗提示
- 最新版本已支持夏活新增的`煌``灰喉`等干员
- 不会帮你点击确定按钮!!!请自行检查辅助选择的是否正确若出现识别错误遗漏了高星干员作者概不负责哦__(:з」∠)_
- 不会帮你点击确定按钮!!!请自行检查软件选择的是否正确若出现识别错误遗漏了高星干员作者概不负责哦__(:з」∠)_
- 自动访问好友
- 访问完了还会贴心的帮你点进信用商店~
- 可设置访问完自动买信用商店的材料
@@ -65,10 +66,11 @@ A game assistance for Arknights
- 所有点击操作,都是点击按钮内随机位置,并模拟泊松分布(按钮偏中间位置点的概率大,越往旁边点到的概率越小)
- 刷理智及访问好友的点击操作,支持设置随机延时,没有封号风险~
- 底层算法纯C++开发并设计了多重的缓存技术最大限度降低CPU和内存占用
- 模拟器窗口可以被遮挡、可以最小化、甚至可以老板键隐藏!即使全屏看视频、玩游戏,也完全不影响辅助运行
- 模拟器窗口可以被遮挡、可以最小化、甚至可以老板键隐藏!即使全屏看视频、玩游戏,也完全不影响软件运行
- 软件支持自动更新✿✿ヽ(°▽°)ノ✿
- 支持多款主流模拟器
- 勉强兼容安卓手机USB调试、无线调试
- 兼容安卓手机USB调试、无线调试
- 兼容非16:9分辨率包括宽屏、方屏也兼容在游戏设置中的异形屏适配基建换班功能除外正在适配中。但仍推荐使用16:9分辨率
- 自适应分辨率及屏幕缩放
- 未来更多功能见[Todo](#Todo)
@@ -88,11 +90,11 @@ A game assistance for Arknights
#### MuMu模拟器
支持
完美支持
#### 雷电模拟器
勉强支持偶尔能用偶尔用不了莫名其妙的有空再修暂时不建议用_(:з」∠)_
支持
#### 逍遥模拟器
@@ -121,7 +123,7 @@ A game assistance for Arknights
1. 根据上面模拟器支持情况,进行对应的`ADB`相关操作
2. 解压压缩包,到**没有中文或特殊符号**的文件夹路径
3. 第一次运行辅助**请使用管理员权限**打开`MeoAsstGui.exe`。运行过一次后,后续不再需要管理员权限(之后的版本会尝试完全去掉管理员权限)
3. 第一次运行软件**请使用管理员权限**打开`MeoAsstGui.exe`。运行过一次后,后续不再需要管理员权限(之后的版本会尝试完全去掉管理员权限)
4. 运行期间,模拟器窗口可以最小化,全屏玩游戏、看视频等,完全不影响
### 刷理智
@@ -131,13 +133,39 @@ A game assistance for Arknights
3. 点击"开始刷理智",开始后上面的选项也可以随时修改
4. 刷完了会自动停止的
### 基建换班
1. 明日方舟处于主界面,或刚进入基建的界面(基建界面没有进行过缩放、干员排序调整等)
2. 根据你的需要勾选左侧需要进行换班的设施(也可以拖动调整换班顺序);设置换班模式、无人机、心情阈值等
3. 点击开始换班,开始执行后上述设置不可修改
#### 换班策略
自动计算并选择单设施内的最优解,支持所有通用类技能和特殊技能组合(但并非跨设施的最优解,如`迷迭香`这类跨设施间联动的体系,暂不支持)
#### 温和换班模式
会对干员人数不满的设施进行换班,计算单设施内最优解,尽量不破坏原有的干员组合;即若设施内干员是满的,则不对该设施进行换班
#### 激进换班模式
会对每一个设施进行换班,计算单设施内最优解,但不会将其他设施中的干员替换过来;即按工作状态排序,仅选择前面的干员
#### 偏激换班模式
会对每一个设施进行换班计算全局的单设施内最优解为追求更高效率会将其他设施内的干员也替换过来即按技能排序计算所有拥有该设施技能的干员效率无论他在不在其他地方工作放心辅助不会傻到把某个干员在几个设施之间来回倒腾的_(:з」∠)_
#### 宿舍入驻心情阈值
计算心情进度条的百分比;心情小于该阈值的干员,无论如何也不上班(不管上面的模式你选择了哪一种),直接进驻宿舍
### 公开招募识别
1. 明日方舟打开公开招募有Tag选择的界面
2. 辅助勾选你需要的选项,点击"开始识别"
2. 软件勾选你需要的选项,点击"开始识别"
3. 请检查识别结果是否正确,自行判断是否确定开始招募
再次强调,本辅助仅会帮你选择最优Tags和时间不会帮你点击确定按钮请自行检查辅助选择的是否正确若出现识别错误遗漏了高星干员作者概不负责哦__(:з」∠)_
再次强调,本软件仅会帮你选择最优Tags和时间不会帮你点击确定按钮请自行检查软件选择的是否正确若出现识别错误遗漏了高星干员作者概不负责哦__(:з」∠)_
### 访问好友
@@ -147,24 +175,16 @@ A game assistance for Arknights
4. 然后会贴心的帮你跳转到信用商店,顺便收了当天信用~
5. 信用商店随缘买,就是从左到右依次买,但不会买`碳``家具零件`
### 不使用模拟器而是USB连接安卓手机
### 连接自定义模拟器端口,或安卓手机/平板
1. 下载[ADB程序](https://dl.google.com/android/repository/platform-tools-latest-windows.zip),将`platform-tools`文件夹解压到`MeoAsstGui.exe`的同级目录
2. 使用USB有线连接安卓手机和电脑
3. 请在手机`设置`-`开发者选项`中打开`USB调试``USB调试安全设置`两个选项。具体操作方式不同品牌手机各不相同,请自行百度查询
4. 请手动修改`resource\config.json`文件中`options`.`connectType``1`。文件保存后请重新打开程序
5. 当前仅兼容刷理智、公招计算功能,且公招计算暂时无法设置时间
4. 请手动修改`resource\config.json`文件中
1. `options`.`connectType`字段值为`1`
2. `emulator`.`Custom`.`adb`.`addresses`字段填写为要连接的地址,请注意这是个数组,会以此尝试所有的(若不填写,或`addresses`中所有的都没连上,则会使用`adb devices`自动查找)
### 不使用模拟器,而是无线连接安卓手机
1. 下载[ADB程序](https://dl.google.com/android/repository/platform-tools-latest-windows.zip),将`platform-tools`文件夹解压到`MeoAsstGui.exe`的同级目录
2. 请保证安卓手机和电脑处于同一局域网中
3. 请在手机`设置`-`开发者选项`中打开`USB调试``USB调试安全设置``无线调试`三个选项。具体操作方式不同品牌手机各不相同,请自行百度查询
4. 请手动修改`resource\config.json`文件中,`options`.`connectType``2`
5. 请手动修改`resource\config.json`文件中,`options`.`connectRemoteAddress`为无线调试的IP地址和端口号。文件保存后请重新打开程序
6. 当前仅兼容刷理智、公招计算功能,且公招计算暂时无法设置时间
**警告:`无线调试`是非常危险的选项若局域网中存在其他攻击性设备或程序可能导致手机数据泄露或丢失请勿在公共的WLAN中使用该功能**
目前非16:9分辨率下刷理智、访问好友、公招识别已初步可用基建功能暂不可用后期将逐步优化。但仍推荐使用16:9分辨率经过的测试验证最多也最稳定。
### 设置操作延时
@@ -179,11 +199,11 @@ A game assistance for Arknights
- [x] 支持设置吃理智药数量
- [x] 信用商店`看着买`功能
- [x] 支持所有主流模拟器
- [ ] adb连接逻辑重构
- [x] adb连接逻辑重构
- [ ] 任务队列功能
- [ ] 常用关卡选关
- [ ] 自动收任务功能
- [ ] 基建智能换班功能
- [x] 基建智能换班功能
- [x] 图形化界面
- [x] 干员技能识别
- [x] 干员识别准确率提高到100%
@@ -192,10 +212,10 @@ A game assistance for Arknights
- [x] 发电站、办公室换班
- [x] 使用无人机
- [ ] 控制中枢智能换班
- [ ] 会客室智能换班
- [ ] 会客室智能线索交流
- [x] 会客室智能换班
- [x] 会客室智能线索交流
- [ ] 支持`迷迭香`等复杂基建体系
- [ ] `激进换班模式`
- [x] `激进换班模式`
- [ ] 自定义换班(手动修改配置文件)
- [ ] 使用GPU进行识别的版本
- [x] 关卡掉落识别
@@ -221,6 +241,7 @@ A game assistance for Arknights
- 文字识别库:[chineseocr_lite](https://github.com/DayBreak-u/chineseocr_lite.git)
- 关卡掉落识别:[企鹅物流识别](https://github.com/KumoSiunaus/penguin-stats-recognize-v3)
- C++ JSON库[meojson](https://github.com/MistEO/meojson.git)
- C++ 运算符解析器:[calculator](https://github.com/kimwalisch/calculator)
- WPF MVVW框架[Stylet](https://github.com/canton7/Stylet)
- WPF控件库[HandyControl](https://github.com/HandyOrg/HandyControl)
- C# JSON库: [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json)
@@ -236,10 +257,13 @@ A game assistance for Arknights
- 非常感谢 [tcyh035](https://github.com/tcyh035) 帮忙设计重构图形界面
- 非常感谢 [GengGode](https://github.com/GengGode) 和 [DbgDebug](https://github.com/DbgDebug) 提供图像算法思路并协助验证
- 非常感谢 [LoveLoliii](https://github.com/LoveLoliii) 提供公开招募算法及数据、部分功能逻辑思路
- 感谢[AAH](https://github.com/ninthDevilHAUNSTER/ArknightsAutoHelper)的大佬们协助提供部分图像、操作思路
- 感谢参与软件测试、提bug的小伙伴们~
- ~~感谢[B站直播间](https://live.bilibili.com/2808861)的小伙伴们陪我弹幕吹水~~
## 广告
[B站直播间](https://live.bilibili.com/2808861):每晚直播敲代码,近期很长一段时间应该都是在写本助软件
[QQ群](https://jq.qq.com/?_wv=1027&k=ypbzXcA2):欢迎加入~
[B站直播间](https://live.bilibili.com/2808861):每晚直播敲代码,近期很长一段时间应该都是在写本助软件
[QQ群](https://jq.qq.com/?_wv=1027&k=ypbzXcA2):欢迎加入~
作者前端苦手寻大佬帮忙一起写界面继续现在的WPF也行用electron或者别的重写也行球球了QAQ

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 60 KiB

View File

@@ -13,15 +13,15 @@ extern "C" {
bool MEOAPI AsstCatchDefault(void* p_asst);
bool MEOAPI AsstCatchEmulator(void* p_asst);
bool MEOAPI AsstCatchUSB(void* p_asst);
bool MEOAPI AsstCatchRemote(void* p_asst, const char* address);
bool MEOAPI AsstCatchCustom(void* p_asst);
bool MEOAPI AsstCatchSpecific(void* p_asst, const char* address);
bool MEOAPI AsstCatchFake(void* p_asst);
bool MEOAPI AsstStartSanity(void* p_asst);
bool MEOAPI AsstStartVisit(void* p_asst, bool with_shopping);
bool MEOAPI AsstStartProcessTask(void* p_asst, const char* task);
bool MEOAPI AsstStartRecruiting(void* p_asst, const int required_level[], int required_len, bool set_time);
bool MEOAPI AsstStartInfrastShift(void* p_asst, const char** order, int order_size, int uses_of_drones, double dorm_threshold);
bool MEOAPI AsstStartInfrastShift(void* p_asst, int work_mode, const char** order, int order_size, int uses_of_drones, double dorm_threshold);
bool MEOAPI AsstStartDebugTask(void* p_asst);
void MEOAPI AsstStop(void* p_asst);

View File

@@ -1,10 +1,8 @@
{
"version": "0.8",
{
"version": "0.9",
"options": {
"connectType": 0,
"connectType_Doc": "连接类型0-连接电脑上的模拟器1-连接USB连接的安卓设备2-连接局域网中的安卓设备。默认0",
"connectRemoteAddress": "",
"connectRemoteAddress_Doc": "连接局域网中的安卓设备的地址仅在connectType为2时生效。格式举例192.168.1.123:5678",
"connectType_Doc": "连接类型0-连接电脑上的模拟器1-连接自定义端口,请手动修改'emulator.Custom'中的相关字段。默认0",
"taskDelay": 1000,
"taskDelay_Doc": "识别的延迟越快识别频率越快但会增加CPU消耗。单位毫秒默认1000",
"printWindow": false,
@@ -20,7 +18,7 @@
"controlDelayRange_Doc": "点击随机延时:每次点击操作会进行随机延时,降低封号风险(好像也没听说过谁被封号的)。格式为 [ 最小延时, 最大延时 ]单位为毫秒。例如想设置3~5秒延时即修改为[ 3000, 5000 ]默认0~0",
"ocrThreadNumber": 4,
"ocrThreadNumber_Doc": "文字识别库OcrLite的线程数量理论上开的高点识别会快点但是会更卡。默认4",
"adbExtraSwipeDist": 20,
"adbExtraSwipeDist": 50,
"adbExtraSwipeDist_Doc": "额外的滑动距离adb有bug同样的参数偶尔会划得非常远。额外做一个短程滑动把之前的停下来",
"adbExtraSwipeDuration": 1000,
"adbExtraSwipeDuration_Doc": "额外的滑动持续时间adb有bug同样的参数偶尔会划得非常远。额外做一个短程滑动把之前的停下来。若小于0则关闭额外滑动功能"
@@ -34,12 +32,15 @@
},
"adb": {
"path": "[EmulatorPath]Engine\\ProgramFiles\\HD-Adb.exe",
"connect": "[Adb] connect 127.0.0.1:5555",
"click": "[Adb] -s 127.0.0.1:5555 shell input tap [x] [y]",
"swipe": "[Adb] -s 127.0.0.1:5555 shell input swipe [x1] [y1] [x2] [y2] [duration]",
"display": "[Adb] -s 127.0.0.1:5555 shell dumpsys window displays | grep init= | awk ' { print $3 } '",
"displayRegex": "cur=%dx%d",
"screencap": "[Adb] -s 127.0.0.1:5555 exec-out screencap -p"
"addresses": [ "127.0.0.1:5555" ],
"devices": "[Adb] devices",
"addressRegex": "(.+)\tdevice",
"connect": "[Adb] connect [Address]",
"click": "[Adb] -s [Address] shell input tap [x] [y]",
"swipe": "[Adb] -s [Address] shell input swipe [x1] [y1] [x2] [y2] [duration]",
"display": "[Adb] -s [Address] shell dumpsys window displays | grep init= | awk ' { print $3 } '",
"displayFormat": "cur=%dx%d",
"screencap": "[Adb] -s [Address] exec-out screencap -p"
}
},
"BlueStacksInternational": {
@@ -49,12 +50,15 @@
},
"adb": {
"path": "[EmulatorPath]HD-Adb.exe",
"connect": "[Adb] connect 127.0.0.1:5555",
"click": "[Adb] -s 127.0.0.1:5555 shell input tap [x] [y]",
"swipe": "[Adb] -s 127.0.0.1:5555 shell input swipe [x1] [y1] [x2] [y2] [duration]",
"display": "[Adb] -s 127.0.0.1:5555 shell dumpsys window displays | grep init= | awk ' { print $3 } '",
"displayRegex": "cur=%dx%d",
"screencap": "[Adb] -s 127.0.0.1:5555 exec-out screencap -p"
"addresses": [ "127.0.0.1:5555" ],
"devices": "[Adb] devices",
"addressRegex": "(.+)\tdevice",
"connect": "[Adb] connect [Address]",
"click": "[Adb] -s [Address] shell input tap [x] [y]",
"swipe": "[Adb] -s [Address] shell input swipe [x1] [y1] [x2] [y2] [duration]",
"display": "[Adb] -s [Address] shell dumpsys window displays | grep init= | awk ' { print $3 } '",
"displayFormat": "cur=%dx%d",
"screencap": "[Adb] -s [Address] exec-out screencap -p"
}
},
"MuMuEmulator": {
@@ -64,42 +68,33 @@
},
"adb": {
"path": "[EmulatorPath]..\\vmonitor\\bin\\adb_server.exe",
"connect": "[Adb] connect 127.0.0.1:7555",
"click": "[Adb] -s 127.0.0.1:7555 shell input tap [x] [y]",
"swipe": "[Adb] -s 127.0.0.1:7555 shell input swipe [x1] [y1] [x2] [y2] [duration]",
"display": "[Adb] -s 127.0.0.1:7555 shell dumpsys window displays | grep init= | awk ' { print $3 } '",
"displayRegex": "cur=%dx%d",
"screencap": "[Adb] -s 127.0.0.1:7555 exec-out screencap -p"
"addresses": [ "127.0.0.1:7555" ],
"devices": "[Adb] devices",
"addressRegex": "(.+)\tdevice",
"connect": "[Adb] connect [Address]",
"click": "[Adb] -s [Address] shell input tap [x] [y]",
"swipe": "[Adb] -s [Address] shell input swipe [x1] [y1] [x2] [y2] [duration]",
"display": "[Adb] -s [Address] shell dumpsys window displays | grep init= | awk ' { print $3 } '",
"displayFormat": "cur=%dx%d",
"screencap": "[Adb] -s [Address] exec-out screencap -p"
}
},
"LDPlayer": {
"handle": {
"class": "LDPlayerMainFrame",
"window": "雷电模拟器"
"window": ""
},
"adb": {
"path": "[EmulatorPath]adb.exe",
"connect": "[Adb] connect 127.0.0.1:5555",
"click": "[Adb] -s emulator-5554 shell input tap [x] [y]",
"swipe": "[Adb] -s emulator-5554 shell input swipe [x1] [y1] [x2] [y2] [duration]",
"display": "[Adb] -s emulator-5554 shell dumpsys window displays | grep init= | awk ' { print $3 } '",
"displayRegex": "cur=%dx%d",
"screencap": "[Adb] -s emulator-5554 shell screencap -p"
}
},
"LDPlayer64": {
"handle": {
"class": "LDPlayerMainFrame",
"window": "雷电模拟器(64)"
},
"adb": {
"path": "[EmulatorPath]adb.exe",
"connect": "[Adb] connect 127.0.0.1:5555",
"click": "[Adb] -s emulator-5554 shell input tap [x] [y]",
"swipe": "[Adb] -s emulator-5554 shell input swipe [x1] [y1] [x2] [y2] [duration]",
"display": "[Adb] -s emulator-5554 shell dumpsys window displays | grep init= | awk ' { print $3 } '",
"displayRegex": "cur=%dx%d",
"screencap": "[Adb] -s emulator-5554 shell screencap -p"
"addresses": [ "127.0.0.1:5555", "emulator-5554" ],
"devices": "[Adb] devices",
"addressRegex": "(.+)\tdevice",
"connect": "[Adb] connect [Address]",
"click": "[Adb] -s [Address] shell input tap [x] [y]",
"swipe": "[Adb] -s [Address] shell input swipe [x1] [y1] [x2] [y2] [duration]",
"display": "[Adb] -s [Address] shell dumpsys window displays | grep init= | awk ' { print $3 } '",
"displayFormat": "cur=%dx%d",
"screencap": "[Adb] -s [Address] shell screencap -p"
}
},
"Nox": {
@@ -109,12 +104,15 @@
},
"adb": {
"path": "[EmulatorPath]nox_adb.exe",
"connect": "[Adb] connect 127.0.0.1:62001",
"click": "[Adb] -s 127.0.0.1:62001 shell input tap [x] [y]",
"swipe": "[Adb] -s 127.0.0.1:62001 shell input swipe [x1] [y1] [x2] [y2] [duration]",
"display": "[Adb] -s 127.0.0.1:62001 shell dumpsys window displays | grep init= | awk ' { print $3 } '",
"displayRegex": "cur=%dx%d",
"screencap": "[Adb] -s 127.0.0.1:62001 exec-out screencap -p"
"addresses": [ "127.0.0.1:62001", "127.0.0.1:59865" ],
"devices": "[Adb] devices",
"addressRegex": "(.+)\tdevice",
"connect": "[Adb] connect [Address]",
"click": "[Adb] -s [Address] shell input tap [x] [y]",
"swipe": "[Adb] -s [Address] shell input swipe [x1] [y1] [x2] [y2] [duration]",
"display": "[Adb] -s [Address] shell dumpsys window displays | grep init= | awk ' { print $3 } '",
"displayFormat": "cur=%dx%d",
"screencap": "[Adb] -s [Address] exec-out screencap -p"
}
},
"XYAZ": {
@@ -124,46 +122,36 @@
},
"adb": {
"path": "[EmulatorPath]adb.exe",
"connect": "[Adb] connect 127.0.0.1:21503",
"click": "[Adb] -s 127.0.0.1:21503 shell input tap [x] [y]",
"swipe": "[Adb] -s 127.0.0.1:21503 shell input swipe [x1] [y1] [x2] [y2] [duration]",
"display": "[Adb] -s 127.0.0.1:21503 shell dumpsys window displays | grep init=",
"displayRegex": " init=%dx%d",
"screencap": "[Adb] -s 127.0.0.1:21503 shell screencap -p"
"addresses": [ "127.0.0.1:21503" ],
"devices": "[Adb] devices",
"addressRegex": "(.+)\tdevice",
"connect": "[Adb] connect [Address]",
"click": "[Adb] -s [Address] shell input tap [x] [y]",
"swipe": "[Adb] -s [Address] shell input swipe [x1] [y1] [x2] [y2] [duration]",
"display": "[Adb] -s [Address] shell dumpsys window displays | grep init=",
"displayFormat": " init=%dx%d",
"screencap": "[Adb] -s [Address] shell screencap -p"
}
},
"Remote": {
"Custom": {
"handle": {
"class": "Remote Class",
"window": "Remote Window",
"class": "Custom Class",
"window": "Custom Window",
"Doc": "这俩不是用来捕获窗口的,是为了兼容现有的捕获模拟器的字段,随手写的,这两个字段实际不会使用到"
},
"adb": {
"Doc": "1.请将Adb解压到'path'中的路径(或者修改'path'2.请修改addresses为指定地址若不填写则使用devices自动查找",
"path": "[ExecDir]platform-tools\\adb.exe",
"addresses": [],
"devices": "[Adb] devices",
"addressRegex": "(.+)\tdevice",
"connect": "[Adb] connect [Address]",
"click": "[Adb] -s [Address] shell input tap [x] [y]",
"swipe": "[Adb] -s [Address] shell input swipe [x1] [y1] [x2] [y2] [duration]",
"display": "[Adb] -s [Address] shell dumpsys window displays | grep init= | awk ' { print $3 } '",
"displayRegex": "cur=%dx%d",
"displayFormat": "cur=%dx%d",
"screencap": "[Adb] -s [Address] exec-out screencap -p"
}
},
"USB": {
"handle": {
"class": "USB Class",
"window": "USB Window",
"Doc": "这俩不是用来捕获窗口的,是为了兼容现有的捕获模拟器的字段,随手写的,这两个字段实际不会使用到"
},
"adb": {
"Doc": "USB连接暂时不兼容多设备的情况",
"path": "[ExecDir]platform-tools\\adb.exe",
"connect": "[Adb] devices",
"click": "[Adb] shell input tap [x] [y]",
"swipe": "[Adb] shell input swipe [x1] [y1] [x2] [y2] [duration]",
"display": "[Adb] shell dumpsys window displays | grep init= | awk ' { print $3 } '",
"displayRegex": "cur=%dx%d",
"screencap": "[Adb] exec-out screencap -p"
}
}
}
}

View File

@@ -1,4 +1,4 @@
{
{
"facility": [
"Mfg",
"Trade",

View File

@@ -1,4 +1,4 @@
[
[
{
"name": "Lancet-2",
"type": "医疗",
@@ -1317,5 +1317,49 @@
"hidden": false,
"name-en": "Ambriel",
"new": true
},
{
"name": "阿",
"type": "特种",
"level": 6,
"sex": "男",
"tags": [
"远程位",
"支援",
"输出",
"高级资深干员"
],
"hidden": false,
"name-en": "Aak",
"new": true
},
{
"name": "吽",
"type": "重装",
"level": 5,
"sex": "男",
"tags": [
"近战位",
"防护",
"治疗",
"资深干员"
],
"hidden": false,
"name-en": "Hung",
"new": true
},
{
"name": "正义骑士号",
"type": "狙击",
"level": 1,
"sex": "女",
"tags": [
"远程位",
"支援",
"支援机械"
],
"hidden": false,
"name-en": "Unknown",
"new": true
}
]

View File

@@ -1,4 +1,4 @@
{
{
"SanityBegin": {
"algorithm": "justreturn",
"action": "doNothing",
@@ -297,7 +297,7 @@
]
},
"Friends": {
"templThreshold": 0.85,
"templThreshold": 0.7,
"action": "clickSelf",
"next": [
"FriendsList",
@@ -391,7 +391,17 @@
]
},
"Mall": {
"algorithm": "OcrDetect",
"action": "clickSelf",
"text": [
"中心"
],
"roi": [
640,
360,
640,
360
],
"next": [
"CreditStore",
"CreditStoreOcr"
@@ -800,7 +810,7 @@
},
"DroneConfirm": {
"action": "clickSelf",
"rearDelay": 2000,
"rearDelay": 3500,
"templThreshold": 0.8,
"next": [
"DeliverableOrder",
@@ -814,7 +824,8 @@
"next": [
"DeliverableOrder",
"DroneAssist-Trade"
]
],
"rearDelay": 2000
},
"DroneAssist-Trade-Stop": {
"Doc": "如果找到了“无人机加速”按钮却没有找到订单交付说明上一次无人机没有把一个订单加速完成也说明没有更多的无人机了所以直接stop",
@@ -905,7 +916,7 @@
300
],
"preDelay": 1000,
"rearDelay": 1000,
"rearDelay": 300,
"Doc": "这里的preDelay作为滑动duration使用rearDelay作为滑动额外延时使用"
},
"InfrastOperListSwipeEnd": {
@@ -1216,8 +1227,8 @@
220
]
},
"InfrastAddOperatorMfg": {
"template": "AddOperatorMfg.png",
"InfrastAddOperatorMfgGentle": {
"template": "AddOperatorMfgGentle.png",
"cache": false,
"roi": [
200,
@@ -1227,8 +1238,25 @@
],
"rearDelay": 1000
},
"InfrastAddOperatorTrade": {
"template": "AddOperatorTrade.png",
"InfrastAddOperatorMfgAggressive": {
"template": "AddOperatorMfgAggressive.png",
"cache": false,
"roi": [
200,
500,
500,
100
],
"rectMove": [
5,
50,
80,
80
],
"rearDelay": 1000
},
"InfrastAddOperatorTradeGentle": {
"template": "AddOperatorTradeGentle.png",
"cache": false,
"roi": [
200,
@@ -1238,8 +1266,25 @@
],
"rearDelay": 1000
},
"InfrastAddOperatorReception": {
"template": "AddOperatorReception.png",
"InfrastAddOperatorTradeAggressive": {
"template": "AddOperatorTradeAggressive.png",
"cache": false,
"roi": [
200,
500,
500,
100
],
"rectMove": [
5,
50,
80,
80
],
"rearDelay": 1000
},
"InfrastAddOperatorReceptionGentle": {
"template": "AddOperatorReceptionGentle.png",
"cache": false,
"roi": [
0,
@@ -1249,19 +1294,31 @@
],
"rearDelay": 1000
},
"InfrastAddOperatorReceptionAggressive": {
"algorithm": "justReturn",
"action": "clickRect",
"specificRect": [
30,
200,
200,
300
],
"rearDelay": 1000
},
"InfrastOperSelected": {
"template": "OperSelected.png",
"template": "empty.png",
"cache": false,
"templThreshold": 0.35,
"templThreshold": 8000,
"templThreshold_Doc": "这个任务中作为符合range的像素点数量阈值",
"maskRange": [
1,
255
95,
105
],
"rectMove": [
0,
-252,
-230,
74,
320
283
]
},
"InfrastOperOnShift": {
@@ -1278,71 +1335,85 @@
"InfrastClueVacancyNo1": {
"template": "ClueVacancyNo1.png",
"cache": false,
"maskRange": [
250,
255
"templThreshold": 0.75,
"roi": [
0,
100,
500,
250
],
"templThreshold": 0.95,
"rearDelay": 1000
},
"InfrastClueVacancyNo2": {
"template": "ClueVacancyNo2.png",
"cache": false,
"maskRange": [
250,
255
"templThreshold": 0.75,
"roi": [
200,
200,
500,
250
],
"templThreshold": 0.95,
"rearDelay": 1000
},
"InfrastClueVacancyNo3": {
"template": "ClueVacancyNo3.png",
"cache": false,
"maskRange": [
250,
255
"templThreshold": 0.75,
"roi": [
400,
100,
500,
250
],
"templThreshold": 0.95,
"rearDelay": 1000
},
"InfrastClueVacancyNo4": {
"template": "ClueVacancyNo4.png",
"cache": false,
"maskRange": [
250,
255
"templThreshold": 0.75,
"roi": [
650,
150,
500,
250
],
"templThreshold": 0.95,
"rearDelay": 1000
},
"InfrastClueVacancyNo5": {
"template": "ClueVacancyNo5.png",
"cache": false,
"maskRange": [
250,
255
"templThreshold": 0.75,
"roi": [
300,
420,
500,
250
],
"templThreshold": 0.95,
"rearDelay": 1000
},
"InfrastClueVacancyNo6": {
"template": "ClueVacancyNo6.png",
"cache": false,
"maskRange": [
250,
255
"templThreshold": 0.75,
"roi": [
500,
350,
500,
250
],
"templThreshold": 0.95,
"rearDelay": 1000
},
"InfrastClueVacancyNo7": {
"template": "ClueVacancyNo7.png",
"cache": false,
"maskRange": [
250,
255
"templThreshold": 0.75,
"roi": [
50,
350,
500,
250
],
"templThreshold": 0.95,
"rearDelay": 1000
},
"InfrastClue": {
@@ -1351,6 +1422,12 @@
"maskRange": [
1,
255
],
"roi": [
800,
0,
480,
720
]
},
"InfrastClueOnTheRightSwipeBegin": {
@@ -1362,8 +1439,8 @@
300,
100
],
"preDelay": 1000,
"rearDelay": 1000,
"preDelay": 500,
"rearDelay": 500,
"maxTimes": 1,
"Doc": "这里的preDelay作为滑动duration使用rearDelay作为滑动额外延时使用maxTimes作为重复次数使用"
},
@@ -1403,8 +1480,10 @@
"action": "clickSelf",
"cache": false,
"next": [
"InfrastClueNew"
]
"InfrastClueNew",
"CloseCluePage"
],
"rearDelay": 2000
},
"GetClue2": {
"template": "GetClue2.png",
@@ -1412,12 +1491,22 @@
"cache": false,
"next": [
"InfrastClueNew"
]
],
"rearDelay": 2000
},
"CloseCluePage": {
"template": "CloseCluePage.png",
"action": "clickSelf",
"cache": false,
"next": [
"InfrastClueNew"
],
"rearDelay": 1000
},
"UnlockClues": {
"template": "UnlockClues.png",
"action": "clickSelf",
"cache": false,
"rearDelay": 2000
"rearDelay": 5000
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -0,0 +1,81 @@
# Visual Studio generated .editorconfig file with C++ settings.
root = true
[*]
indent_style = space
insert_final_newline = true
charset = utf-8
end_of_line = crlf
[*.json]
indent_size = 4
[*.{c++,cc,cpp,cppm,cxx,h,h++,hh,hpp,hxx,inl,ipp,ixx,tlh,tli}]
indent_size = 4
# Visual C++ Code Style settings
cpp_generate_documentation_comments = xml
# Visual C++ Formatting settings
cpp_indent_braces = false
cpp_indent_multi_line_relative_to = innermost_parenthesis
cpp_indent_within_parentheses = indent
cpp_indent_preserve_within_parentheses = true
cpp_indent_case_contents = true
cpp_indent_case_labels = false
cpp_indent_case_contents_when_block = false
cpp_indent_lambda_braces_when_parameter = true
cpp_indent_goto_labels = one_left
cpp_indent_preprocessor = leftmost_column
cpp_indent_access_specifiers = false
cpp_indent_namespace_contents = true
cpp_indent_preserve_comments = false
cpp_new_line_before_open_brace_namespace = new_line
cpp_new_line_before_open_brace_type = new_line
cpp_new_line_before_open_brace_function = new_line
cpp_new_line_before_open_brace_block = same_line
cpp_new_line_before_open_brace_lambda = same_line
cpp_new_line_scope_braces_on_separate_lines = false
cpp_new_line_close_brace_same_line_empty_type = false
cpp_new_line_close_brace_same_line_empty_function = false
cpp_new_line_before_catch = true
cpp_new_line_before_else = true
cpp_new_line_before_while_in_do_while = false
cpp_space_before_function_open_parenthesis = remove
cpp_space_within_parameter_list_parentheses = false
cpp_space_between_empty_parameter_list_parentheses = false
cpp_space_after_keywords_in_control_flow_statements = true
cpp_space_within_control_flow_statement_parentheses = false
cpp_space_before_lambda_open_parenthesis = false
cpp_space_within_cast_parentheses = false
cpp_space_after_cast_close_parenthesis = false
cpp_space_within_expression_parentheses = false
cpp_space_before_block_open_brace = true
cpp_space_between_empty_braces = false
cpp_space_before_initializer_list_open_brace = false
cpp_space_within_initializer_list_braces = true
cpp_space_preserve_in_initializer_list = true
cpp_space_before_open_square_bracket = false
cpp_space_within_square_brackets = false
cpp_space_before_empty_square_brackets = false
cpp_space_between_empty_square_brackets = false
cpp_space_group_square_brackets = true
cpp_space_within_lambda_brackets = false
cpp_space_between_empty_lambda_brackets = false
cpp_space_before_comma = false
cpp_space_after_comma = true
cpp_space_remove_around_member_operators = true
cpp_space_before_inheritance_colon = true
cpp_space_before_constructor_colon = true
cpp_space_remove_before_semicolon = true
cpp_space_after_semicolon = false
cpp_space_remove_around_unary_operator = true
cpp_space_around_binary_operator = insert
cpp_space_around_assignment_operator = insert
cpp_space_pointer_reference_alignment = left
cpp_space_around_ternary_operator = insert
cpp_wrap_preserve_blocks = one_liners

View File

@@ -1,4 +1,4 @@
#include "AbstractConfiger.h"
#include "AbstractConfiger.h"
#include <json.h>
@@ -24,4 +24,4 @@ bool asst::AbstractConfiger::load(const std::string& filename)
return false;
}
return true;
}
}

View File

@@ -1,19 +1,22 @@
#pragma once
#pragma once
#include "AbstractResource.h"
#include <string>
namespace json {
namespace json
{
class value;
}
namespace asst {
namespace asst
{
class AbstractConfiger : public AbstractResource
{
public:
virtual ~AbstractConfiger() = default;
virtual bool load(const std::string& filename) override;
protected:
virtual bool parse(const json::value& json) = 0;
};

View File

@@ -1,16 +1,15 @@
#include "AbstractImageAnalyzer.h"
#include "AbstractImageAnalyzer.h"
#include "AsstUtils.hpp"
#include "Controller.h"
asst::AbstractImageAnalyzer::AbstractImageAnalyzer(const cv::Mat& image)
: m_image(image)
, m_roi(empty_rect_to_full(Rect(), image))
: m_image(image), m_roi(empty_rect_to_full(Rect(), image))
#ifdef LOG_TRACE
,
m_image_draw(image.clone())
#endif
{
}
{}
asst::AbstractImageAnalyzer::AbstractImageAnalyzer(const cv::Mat& image, const Rect& roi)
: m_image(image),
@@ -42,6 +41,11 @@ void asst::AbstractImageAnalyzer::set_roi(const Rect& roi) noexcept
m_roi = empty_rect_to_full(roi, m_image);
}
void asst::AbstractImageAnalyzer::correct_roi() noexcept
{
m_roi = ctrler.shaped_correct(m_roi);
}
asst::Rect asst::AbstractImageAnalyzer::empty_rect_to_full(const Rect& rect, const cv::Mat& image) noexcept
{
return rect.empty() ? Rect(0, 0, image.cols, image.rows) : rect;
@@ -73,4 +77,4 @@ std::string asst::AbstractImageAnalyzer::calc_hash(const Rect& roi) const
pix++;
}
return hash_value.str();
}
}

View File

@@ -1,10 +1,11 @@
#pragma once
#pragma once
#include <opencv2/opencv.hpp>
#include "AsstDef.h"
namespace asst {
namespace asst
{
class AbstractImageAnalyzer
{
public:
@@ -19,9 +20,10 @@ namespace asst {
virtual void set_image(const cv::Mat& image, const Rect& roi);
virtual void set_roi(const Rect& roi) noexcept;
virtual bool analyze() = 0;
virtual void correct_roi() noexcept;
std::string calc_hash() const; // 使用m_roi
std::string calc_hash(const Rect& roi) const;// 使用参数roi
std::string calc_hash() const; // 使用m_roi
std::string calc_hash(const Rect& roi) const; // 使用参数roi
AbstractImageAnalyzer& operator=(const AbstractImageAnalyzer&) = delete;
AbstractImageAnalyzer& operator=(AbstractImageAnalyzer&&) = delete;
@@ -36,4 +38,4 @@ namespace asst {
cv::Mat m_image_draw;
#endif
};
}
}

View File

@@ -1,8 +1,9 @@
#pragma once
#pragma once
#include <string>
namespace asst {
namespace asst
{
class AbstractResource
{
public:

View File

@@ -1,4 +1,4 @@
#include "AbstractTask.h"
#include "AbstractTask.h"
#include <algorithm>
#include <filesystem>
@@ -6,8 +6,8 @@
#include <opencv2/opencv.hpp>
#include "Controller.h"
#include "AsstUtils.hpp"
#include "Controller.h"
#include "Logger.hpp"
#include "Resource.h"
@@ -42,7 +42,8 @@ bool AbstractTask::sleep(unsigned millisecond)
while (!need_exit() && duration < millisecond) {
duration = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now() - start).count();
std::chrono::system_clock::now() - start)
.count();
std::this_thread::yield();
}
m_callback(AsstMsg::EndOfSleep, callback_json, m_callback_arg);
@@ -81,4 +82,4 @@ void asst::AbstractTask::click_return_button()
ctrler.click(ReturnButtonRect);
sleep(return_task_ptr->rear_delay);
}
}

View File

@@ -1,4 +1,4 @@
#pragma once
#pragma once
#include <memory>
@@ -9,7 +9,8 @@ namespace cv
class Mat;
}
namespace asst {
namespace asst
{
class AbstractTask
{
public:
@@ -26,6 +27,7 @@ namespace asst {
virtual void set_task_chain(std::string name) { m_task_chain = std::move(name); }
virtual const std::string& get_task_chain() { return m_task_chain; }
virtual void on_run_fails(int retry_times) { ; }
protected:
virtual bool sleep(unsigned millisecond);
virtual bool save_image(const cv::Mat& iamge, const std::string& dir);
@@ -39,4 +41,4 @@ namespace asst {
std::string m_task_chain;
int m_retry_times = INT_MAX;
};
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,23 +1,25 @@
#pragma once
#pragma once
#include <thread>
#include <mutex>
#include <condition_variable>
#include <memory>
#include <optional>
#include <unordered_map>
#include <queue>
#include <deque>
#include <memory>
#include <mutex>
#include <optional>
#include <queue>
#include <thread>
#include <unordered_map>
#include "AbstractTask.h"
#include "AsstDef.h"
#include "AsstMsg.h"
#include "AbstractTask.h"
namespace cv {
namespace cv
{
class Mat;
}
namespace asst {
namespace asst
{
class Controller;
class Identify;
@@ -32,9 +34,9 @@ namespace asst {
// 捕获模拟器
bool catch_emulator(const std::string& emulator_name = std::string());
// 捕获usb设备
bool catch_usb();
bool catch_custom();
// 捕获远程地址(安卓手机)
bool catch_remote(const std::string& address);
bool catch_specific(const std::string& address);
// 不实际进行捕获,调试用接口
bool catch_fake();
@@ -46,7 +48,7 @@ namespace asst {
// 开始公开招募操作
bool start_recruiting(const std::vector<int>& required_level, bool set_time = true);
// 开始基建换班任务
bool start_infrast_shift(const std::vector<std::string>& order, UsesOfDrones uses, double dorm_threshold);
bool start_infrast_shift(InfrastWorkMode work_mode, const std::vector<std::string>& order, UsesOfDrones uses, double dorm_threshold);
// 开始流程任务应该是private的调试用临时放到public
bool start_process_task(const std::string& task, int retry_times = ProcessTaskRetryTimesDefault, bool block = true);
@@ -71,7 +73,7 @@ namespace asst {
void append_task(const json::value& detail, bool front = false);
void append_callback(AsstMsg msg, json::value detail);
void clear_exec_times();
json::value organize_stage_drop(const json::value& rec); // 整理关卡掉落的材料信息
json::value organize_stage_drop(const json::value& rec); // 整理关卡掉落的材料信息
bool m_inited = false;
@@ -90,4 +92,4 @@ namespace asst {
std::mutex m_msg_mutex;
std::condition_variable m_msg_condvar;
};
}
}

View File

@@ -1,12 +1,13 @@
#include "AsstCaller.h"
#include "AsstCaller.h"
#include <string.h>
#include <json_value.h>
#include "Version.h"
#include "AsstUtils.hpp"
#include "Assistance.h"
#include "AsstDef.h"
#include "AsstUtils.hpp"
#include "Version.h"
#if 0
#if _MSC_VER
@@ -14,7 +15,8 @@
BOOL APIENTRY DllMain(HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
) {
)
{
UNREFERENCED_PARAMETER(hModule);
UNREFERENCED_PARAMETER(lpReserved);
switch (ul_reason_for_call) {
@@ -88,35 +90,35 @@ bool AsstCatchEmulator(void* p_asst)
return ((asst::Assistance*)p_asst)->catch_emulator();
}
bool AsstCatchUSB(void* p_asst)
bool AsstCatchCustom(void* p_asst)
{
if (p_asst == nullptr) {
return false;
}
return ((asst::Assistance*)p_asst)->catch_usb();
return ((asst::Assistance*)p_asst)->catch_custom();
}
bool AsstCatchRemote(void* p_asst, const char* address)
bool AsstCatchSpecific(void* p_asst, const char* address)
{
if (p_asst == nullptr) {
return false;
}
return ((asst::Assistance*)p_asst)->catch_remote(address);
}
bool MEOAPI AsstCatchFake(void* p_asst)
return ((asst::Assistance*)p_asst)->catch_specific(address);
}
bool AsstCatchFake(void* p_asst)
{
#ifdef LOG_TRACE
if (p_asst == nullptr) {
return false;
}
return ((asst::Assistance*)p_asst)->catch_fake();
#else
return false;
#endif // LOG_TRACE
return ((asst::Assistance*)p_asst)->catch_fake();
#else
return false;
#endif // LOG_TRACE
}
bool AsstStartSanity(void* p_asst)
@@ -179,7 +181,7 @@ bool AsstStartRecruiting(void* p_asst, const int required_level[], int required_
return ((asst::Assistance*)p_asst)->start_recruiting(level_vector, set_time);
}
bool AsstStartInfrastShift(void* p_asst, const char** order, int order_size, int uses_of_drones, double dorm_threshold)
bool AsstStartInfrastShift(void* p_asst, int work_mode, const char** order, int order_size, int uses_of_drones, double dorm_threshold)
{
if (p_asst == nullptr) {
return false;
@@ -187,10 +189,7 @@ bool AsstStartInfrastShift(void* p_asst, const char** order, int order_size, int
std::vector<std::string> order_vector;
order_vector.assign(order, order + order_size);
return ((asst::Assistance*)p_asst)->start_infrast_shift(
order_vector,
static_cast<asst::UsesOfDrones>(uses_of_drones),
dorm_threshold);
return ((asst::Assistance*)p_asst)->start_infrast_shift(static_cast<asst::InfrastWorkMode>(work_mode), order_vector, static_cast<asst::UsesOfDrones>(uses_of_drones), dorm_threshold);
}
bool AsstStartDebugTask(void* p_asst)
@@ -203,4 +202,4 @@ bool AsstStartDebugTask(void* p_asst)
#else
return false;
#endif // LOG_TRACE
}
}

View File

@@ -1,20 +1,27 @@
#pragma once
#pragma once
#include <functional>
#include <ostream>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <ostream>
#include <functional>
namespace json
{
class value;
}
namespace asst {
namespace asst
{
constexpr double DoubleDiff = 1e-12;
constexpr static int WindowWidthDefault = 1280;
constexpr static int WindowHeightDefault = 720;
constexpr static double TemplThresholdDefault = 0.9;
constexpr static double HistThresholdDefault = 0.9;
struct Point
{
Point() = default;
@@ -33,7 +40,8 @@ namespace asst {
Rect(const Rect&) noexcept = default;
Rect(Rect&&) noexcept = default;
Rect(int x, int y, int width, int height)
: x(x), y(y), width(width), height(height) {}
: x(x), y(y), width(width), height(height)
{}
Rect operator*(double rhs) const
{
return { x, y, static_cast<int>(width * rhs), static_cast<int>(height * rhs) };
@@ -43,11 +51,19 @@ namespace asst {
int half_width_scale = static_cast<int>(width * (1 - scale) / 2);
int half_hight_scale = static_cast<int>(height * (1 - scale) / 2);
Rect dst(x + half_width_scale, y + half_hight_scale,
static_cast<int>(width * scale), static_cast<int>(height * scale));
if (dst.x < 0) { dst.x = 0; }
if (dst.y < 0) { dst.y = 0; }
if (dst.width + dst.x >= max_width) { dst.width = max_width - dst.x; }
if (dst.height + dst.y >= max_height) { dst.height = max_height - dst.y; }
static_cast<int>(width * scale), static_cast<int>(height * scale));
if (dst.x < 0) {
dst.x = 0;
}
if (dst.y < 0) {
dst.y = 0;
}
if (dst.width + dst.x >= max_width) {
dst.width = max_width - dst.x;
}
if (dst.height + dst.y >= max_height) {
dst.height = max_height - dst.y;
}
return dst;
}
Rect& operator=(const Rect&) noexcept = default;
@@ -59,16 +75,11 @@ namespace asst {
}
bool include(const Rect& rhs) const noexcept
{
return x <= rhs.x
&& y <= rhs.y
&& (x + width) >= (rhs.x + rhs.width)
&& (y + height) >= (rhs.y + rhs.height);
return x <= rhs.x && y <= rhs.y && (x + width) >= (rhs.x + rhs.width) && (y + height) >= (rhs.y + rhs.height);
}
std::string to_string() const {
return "[ " + std::to_string(x) + ", "
+ std::to_string(y) + ", "
+ std::to_string(width) + ", "
+ std::to_string(height) + " ]";
std::string to_string() const
{
return "[ " + std::to_string(x) + ", " + std::to_string(y) + ", " + std::to_string(width) + ", " + std::to_string(height) + " ]";
}
int x = 0;
@@ -77,19 +88,22 @@ namespace asst {
int height = 0;
};
struct TextRect {
struct TextRect
{
TextRect() = default;
TextRect(const TextRect&) = default;
TextRect(TextRect&&) noexcept = default;
explicit operator std::string() const noexcept { return text; }
explicit operator Rect() const noexcept { return rect; }
std::string to_string() const {
std::string to_string() const
{
return text + " : " + rect.to_string();
}
TextRect& operator=(const TextRect&) = default;
TextRect& operator=(TextRect&&) noexcept = default;
bool operator==(const TextRect& rhs) const noexcept {
bool operator==(const TextRect& rhs) const noexcept
{
return text == rhs.text && rect == rhs.rect;
}
@@ -98,7 +112,8 @@ namespace asst {
};
using TextRectProc = std::function<bool(TextRect&)>;
enum class AlgorithmType {
enum class AlgorithmType
{
Invaild = -1,
JustReturn,
MatchTemplate,
@@ -106,7 +121,8 @@ namespace asst {
OcrDetect
};
struct MatchRect {
struct MatchRect
{
MatchRect() = default;
MatchRect(const MatchRect&) = default;
MatchRect(MatchRect&&) noexcept = default;
@@ -121,105 +137,115 @@ namespace asst {
};
}
namespace std {
template<>
class hash<asst::Rect> {
namespace std
{
template <>
class hash<asst::Rect>
{
public:
size_t operator()(const asst::Rect& rect) const
{
return std::hash<int>()(rect.x)
^ std::hash<int>()(rect.y)
^ std::hash<int>()(rect.width)
^ std::hash<int>()(rect.height);
return std::hash<int>()(rect.x) ^ std::hash<int>()(rect.y) ^ std::hash<int>()(rect.width) ^ std::hash<int>()(rect.height);
}
};
template<>
class hash<asst::TextRect> {
template <>
class hash<asst::TextRect>
{
public:
size_t operator()(const asst::TextRect& tr) const
{
return std::hash<std::string>()(tr.text)
^ std::hash<asst::Rect>()(tr.rect);
return std::hash<std::string>()(tr.text) ^ std::hash<asst::Rect>()(tr.rect);
}
};
}
namespace asst {
enum class ProcessTaskAction {
namespace asst
{
enum class ProcessTaskAction
{
Invalid = 0,
BasicClick = 0x100,
ClickSelf = BasicClick | 1, // 点击自身位置
ClickRect = BasicClick | 2, // 点击指定区域
ClickRand = BasicClick | 4, // 点击随机区域
DoNothing = 0x200, // 什么都不做
Stop = 0x400, // 停止当前Task
StageDrops = 0x800, // 关卡结束,特化动作
ClickSelf = BasicClick | 1, // 点击自身位置
ClickRect = BasicClick | 2, // 点击指定区域
ClickRand = BasicClick | 4, // 点击随机区域
DoNothing = 0x200, // 什么都不做
Stop = 0x400, // 停止当前Task
StageDrops = 0x800, // 关卡结束,特化动作
BasicSwipe = 0x1000,
SwipeToTheLeft = BasicSwipe | 1, // 往左划一下
SwipeToTheRight = BasicSwipe | 2, // 往右划一下
SwipeToTheLeft = BasicSwipe | 1, // 往左划一下
SwipeToTheRight = BasicSwipe | 2, // 往右划一下
};
// 任务信息
struct TaskInfo {
struct TaskInfo
{
virtual ~TaskInfo() = default;
std::string name; // 任务名
AlgorithmType algorithm = // 图像算法类型
std::string name; // 任务名
AlgorithmType algorithm = // 图像算法类型
AlgorithmType::Invaild;
ProcessTaskAction action = // 要进行的操作
ProcessTaskAction action = // 要进行的操作
ProcessTaskAction::Invalid;
std::vector<std::string> next; // 下一个可能的任务(列表)
int exec_times = 0; // 任务已执行了多少次
int max_times = INT_MAX; // 任务最多执行多少次
std::vector<std::string> exceeded_next; // 达到最多次数了之后,下一个可能的任务(列表)
std::vector<std::string> reduce_other_times; // 执行了该任务后,需要减少别的任务的执行次数。例如执行了吃理智药,则说明上一次点击蓝色开始行动按钮没生效,所以蓝色开始行动要-1
asst::Rect specific_rect; // 指定区域目前仅针对ClickRect任务有用会点这个区域
int pre_delay = 0; // 执行该任务前的延时
int rear_delay = 0; // 执行该任务后的延时
int retry_times = INT_MAX; // 未找到图像时的重试次数
Rect roi; // 要识别的区域若为0则全图识别
Rect rect_move; // 识别结果移动有些结果识别到的和要点击的不是同一个位置。即识别到了res点击res + result_move的位置
std::vector<std::string> next; // 下一个可能的任务(列表)
int exec_times = 0; // 任务已执行了多少次
int max_times = INT_MAX; // 任务最多执行多少次
std::vector<std::string> exceeded_next; // 达到最多次数了之后,下一个可能的任务(列表)
std::vector<std::string> reduce_other_times; // 执行了该任务后,需要减少别的任务的执行次数。例如执行了吃理智药,则说明上一次点击蓝色开始行动按钮没生效,所以蓝色开始行动要-1
asst::Rect specific_rect; // 指定区域目前仅针对ClickRect任务有用会点这个区域
int pre_delay = 0; // 执行该任务前的延时
int rear_delay = 0; // 执行该任务后的延时
int retry_times = INT_MAX; // 未找到图像时的重试次数
Rect roi; // 要识别的区域若为0则全图识别
Rect rect_move; // 识别结果移动有些结果识别到的和要点击的不是同一个位置。即识别到了res点击res + result_move的位置
};
// 文字识别任务的信息
struct OcrTaskInfo : public TaskInfo {
struct OcrTaskInfo : public TaskInfo
{
virtual ~OcrTaskInfo() = default;
std::vector<std::string> text; // 文字的容器,匹配到这里面任一个,就算匹配上了
bool need_full_match = false; // 是否需要全匹配,否则搜索到子串就算匹配上了
std::vector<std::string> text; // 文字的容器,匹配到这里面任一个,就算匹配上了
bool need_full_match = false; // 是否需要全匹配,否则搜索到子串就算匹配上了
std::unordered_map<std::string, std::string>
replace_map; // 部分文字容易识别错字符串强制replace之后再进行匹配
bool cache = false; // 是否使用历史区域
std::unordered_set<Rect> region_of_appeared; // 曾经出现过的区域上次处理该任务时在一些rect里识别到过text这次优先在这些rect里识别省点性能
replace_map; // 部分文字容易识别错字符串强制replace之后再进行匹配
bool cache = false; // 是否使用历史区域
std::unordered_set<Rect> region_of_appeared; // 曾经出现过的区域上次处理该任务时在一些rect里识别到过text这次优先在这些rect里识别省点性能
};
// 图片匹配任务的信息
struct MatchTaskInfo : public TaskInfo {
struct MatchTaskInfo : public TaskInfo
{
virtual ~MatchTaskInfo() = default;
std::string templ_name; // 匹配模板图片文件名
double templ_threshold = 0; // 模板匹配阈值
double hist_threshold = 0; // 直方图比较阈值
std::pair<int, int> mask_range; // 掩码的二值化范围
bool cache = false; // 是否使用缓存直方图false时就一直用模板匹配。默认为true
std::string templ_name; // 匹配模板图片文件名
double templ_threshold = 0; // 模板匹配阈值
double hist_threshold = 0; // 直方图比较阈值
std::pair<int, int> mask_range; // 掩码的二值化范围
bool cache = false; // 是否使用缓存直方图false时就一直用模板匹配。默认为true
};
struct HandleInfo {
struct HandleInfo
{
std::string class_name;
std::string window_name;
};
struct AdbCmd {
struct AdbCmd
{
std::string path;
std::vector<std::string> addresses; // 会优先尝试连接addresses中的地址若均失败则会使用devices获取地址
std::string devices;
std::string address_regex;
std::string connect;
std::string click;
std::string swipe;
std::string display;
std::string display_regex;
std::string display_format;
std::string screencap;
//std::string pullscreen;
int display_width = 0;
int display_height = 0;
};
struct EmulatorInfo {
struct EmulatorInfo
{
std::string name;
HandleInfo handle;
AdbCmd adb;
@@ -229,50 +255,58 @@ namespace asst {
/* 基建相关 */
// 设施信息
struct InfrastFacilityInfo {
struct InfrastFacilityInfo
{
std::string id;
std::vector<std::string> products;
int max_num_of_opers = 0;
};
enum class InfrastSmileyType {
enum class InfrastSmileyType
{
Invalid = -1,
Rest, // 休息完成,绿色笑脸
Work, // 工作中,黄色笑脸
Distract // 注意力涣散,红色哭脸
Rest, // 休息完成,绿色笑脸
Work, // 工作中,黄色笑脸
Distract // 注意力涣散,红色哭脸
};
struct InfrastSmileyInfo {
struct InfrastSmileyInfo
{
InfrastSmileyType type;
Rect rect;
};
struct InfrastOperMoodInfo {
std::string hash; // 干员部分立绘的hash作为唯一标识
struct InfrastOperMoodInfo
{
std::string hash; // 干员部分立绘的hash作为唯一标识
InfrastSmileyInfo smiley;
double percentage = 0; // 心情进度条的百分比
double percentage = 0; // 心情进度条的百分比
Rect rect;
bool working = false; // 干员是否在工作中
bool selected = false; // 干员是否已被选择(蓝色的选择框)
bool working = false; // 干员是否在工作中
bool selected = false; // 干员是否已被选择(蓝色的选择框)
};
struct InfrastSkill {
struct InfrastSkill
{
std::string id;
std::string templ_name;
std::vector<std::string> names; // 很多基建技能是一样的就是名字不同。所以一个技能id可能对应多个名字
std::vector<std::string> names; // 很多基建技能是一样的就是名字不同。所以一个技能id可能对应多个名字
std::string intro;
std::unordered_map<std::string, double>
efficient; // 技能效率key产品名赤金、经验书等, value: 效率数值
efficient; // 技能效率key产品名赤金、经验书等, value: 效率数值
std::unordered_map<std::string, std::string>
efficient_regex; // 技能效率正则key产品名赤金、经验书等, value: 效率正则。如不为空会先对正则进行计算再加上efficient里面的值
efficient_regex; // 技能效率正则key产品名赤金、经验书等, value: 效率正则。如不为空会先对正则进行计算再加上efficient里面的值
bool operator==(const InfrastSkill& skill) const noexcept {
bool operator==(const InfrastSkill& skill) const noexcept
{
return id == skill.id;
}
};
}
namespace std {
template<>
class hash<asst::InfrastSkill> {
namespace std
{
template <>
class hash<asst::InfrastSkill>
{
public:
size_t operator()(const asst::InfrastSkill& skill) const
{
@@ -284,9 +318,11 @@ namespace std {
namespace asst
{
// 基建单个干员的技能
struct InfrastSkillsComb {
struct InfrastSkillsComb
{
InfrastSkillsComb() = default;
InfrastSkillsComb(std::unordered_set<InfrastSkill> skill_vec) {
InfrastSkillsComb(std::unordered_set<InfrastSkill> skill_vec)
{
skills = std::move(skill_vec);
for (const auto& s : skills) {
for (const auto& [key, value] : s.efficient) {
@@ -297,7 +333,8 @@ namespace asst
}
}
}
bool operator==(const InfrastSkillsComb& rhs) const {
bool operator==(const InfrastSkillsComb& rhs) const
{
return skills == rhs.skills;
}
@@ -307,20 +344,30 @@ namespace asst
std::unordered_map<std::string, std::string> efficient_regex;
};
// 基建 干员技能信息
struct InfrastOperSkillInfo {
struct InfrastOperSkillInfo
{
InfrastOperSkillInfo() = default;
InfrastOperSkillInfo(InfrastSkillsComb skills_comb) : skills_comb(std::move(skills_comb)) {}
std::string hash; // 有些干员的技能是完全一样的做个hash区分一下不同干员
std::string hash; // 有些干员的技能是完全一样的做个hash区分一下不同干员
InfrastSkillsComb skills_comb;
Rect rect;
bool selected = false; // 干员是否已被选择(蓝色的选择框)
bool selected = false; // 干员是否已被选择(蓝色的选择框)
};
// 基建技能组
struct InfrastSkillsGroup {
std::string intro; // 文字介绍,实际不起作用
std::unordered_map<std::string, int> conditions; // 技能组合可用条件例如key 发电站数量value 3
std::vector<InfrastSkillsComb> necessary; // 必选技能。这里面的缺少任一,则该技能组合可用
std::vector<InfrastSkillsComb> optional; // 选技能。
bool allow_external = false; // 当干员数没满3个的时候是否允许补充外部干员
struct InfrastSkillsGroup
{
std::string intro; // 文字介绍,实际不起作用
std::unordered_map<std::string, int> conditions; // 技能组合可用条件例如key 发电站数量value 3
std::vector<InfrastSkillsComb> necessary; // 选技能。这里面的缺少任一,则该技能组合不可用
std::vector<InfrastSkillsComb> optional; // 可选技能。
bool allow_external = false; // 当干员数没满3个的时候是否允许补充外部干员
};
}
enum class InfrastWorkMode
{
Invaild = -1,
Gentle, // 温和换班模式:会对干员人数不满的设施进行换班,计算单设施内最优解,尽量不破坏原有的干员组合;即若设施内干员是满的,则不对该设施进行换班
Aggressive, // 激进换班模式:会对每一个设施进行换班,计算单设施内最优解,但不会将其他设施中的干员替换过来;即按工作状态排序,仅选择前面的干员
Extreme // 偏激换班模式:会对每一个设施进行换班,计算全局的单设施内最优解,为追求更高效率,会将其他设施内的干员也替换过来;即按技能排序,计算所有拥有该设施技能的干员效率,无论他在不在其他地方工作
};
}

View File

@@ -1,95 +1,92 @@
#pragma once
#pragma once
#include <ostream>
#include <memory>
#include <unordered_map>
#include <functional>
#include <memory>
#include <ostream>
#include <unordered_map>
#include <json.h>
namespace asst {
enum class AsstMsg {
namespace asst
{
enum class AsstMsg
{
/* Error Msg */
PtrIsNull, // 指针为空
ImageIsEmpty, // 图像为空
WindowMinimized, // [已弃用] 窗口被最小化了
InitFaild, // 初始化失败
TaskError, // 任务错误任务一直出错retry次数达到上限
OcrResultError, // Ocr识别结果错误
PtrIsNull, // 指针为空
ImageIsEmpty, // 图像为空
WindowMinimized, // [已弃用] 窗口被最小化了
InitFaild, // 初始化失败
TaskError, // 任务错误任务一直出错retry次数达到上限
OcrResultError, // Ocr识别结果错误
/* Info Msg: about Task */
TaskStart = 1000, // 任务开始
TaskMatched, // 任务匹配成功
ReachedLimit, // 单个原子任务达到次数上限
ReadyToSleep, // 准备开始睡眠
EndOfSleep, // 睡眠结束
AppendProcessTask, // 新增流程任务Assistance内部消息外部不需要处理
AppendTask, // 新增任务Assistance内部消息外部不需要处理
TaskCompleted, // 单个原子任务完成
PrintWindow, // 截图消息
ProcessTaskStopAction, // 流程任务执行到了Stop的动作
TaskChainCompleted, // 任务链完成
ProcessTaskNotMatched, // 流程任务识别错误
TaskStart = 1000, // 任务开始
TaskMatched, // 任务匹配成功
ReachedLimit, // 单个原子任务达到次数上限
ReadyToSleep, // 准备开始睡眠
EndOfSleep, // 睡眠结束
AppendProcessTask, // 新增流程任务Assistance内部消息外部不需要处理
AppendTask, // 新增任务Assistance内部消息外部不需要处理
TaskCompleted, // 单个原子任务完成
PrintWindow, // 截图消息
ProcessTaskStopAction, // 流程任务执行到了Stop的动作
TaskChainCompleted, // 任务链完成
ProcessTaskNotMatched, // 流程任务识别错误
/* Info Msg: about Identify */
TextDetected = 2000, // 识别到文字
ImageFindResult, // 查找图像的结果
ImageMatched, // 图像匹配成功
StageDrops, // 关卡掉落信息
TextDetected = 2000, // 识别到文字
ImageFindResult, // 查找图像的结果
ImageMatched, // 图像匹配成功
StageDrops, // 关卡掉落信息
/* Open Recruit Msg */
RecruitTagsDetected = 3000, // 公招识别到了Tags
RecruitSpecialTag, // 公招识别到了特殊的Tag
RecruitResult, // 公开招募结果
RecruitTagsDetected = 3000, // 公招识别到了Tags
RecruitSpecialTag, // 公招识别到了特殊的Tag
RecruitResult, // 公开招募结果
/* Infrast Msg */
OpersDetected = 4000, // 识别到了干员s
OpersIdtfResult, // 干员识别结果(总的)
InfrastComb, // 当前房间的最优干员组合
EnterStation, // 进入某个房间
StationInfo, // 当前房间信息
ReadyToShift, // 准备换班
ShiftCompleted, // 换班完成(单个房间)
NoNeedToShift // 无需换班(单个房间)
InfrastSkillsDetected = 4000, // 识别到了基建技能(当前页面)
InfrastSkillsResult, // 识别到的所有可用技能
InfrastComb, // 当前房间的最优干员组合
EnterFacility, // 进入某个房间
FacilityInfo, // 当前设施信息
};
static std::ostream& operator<<(std::ostream& os, const AsstMsg& type)
{
static const std::unordered_map<AsstMsg, std::string> _type_name = {
{AsstMsg::PtrIsNull, "PtrIsNull"},
{AsstMsg::ImageIsEmpty, "ImageIsEmpty"},
{AsstMsg::WindowMinimized, "WindowMinimized"},
{AsstMsg::InitFaild, "InitFaild"},
{AsstMsg::TaskStart, "TaskStart"},
{AsstMsg::ImageFindResult, "ImageFindResult"},
{AsstMsg::ImageMatched, "ImageMatched"},
{AsstMsg::StageDrops, "StageDrops"},
{AsstMsg::TaskMatched, "TaskMatched"},
{AsstMsg::ReachedLimit, "ReachedLimit"},
{AsstMsg::ReadyToSleep, "ReadyToSleep"},
{AsstMsg::EndOfSleep, "EndOfSleep"},
{AsstMsg::AppendProcessTask, "AppendProcessTask"},
{AsstMsg::TaskCompleted, "TaskCompleted"},
{AsstMsg::TaskChainCompleted, "TaskChainCompleted"},
{AsstMsg::PrintWindow, "PrintWindow"},
{AsstMsg::TaskError, "TaskError"},
{AsstMsg::ProcessTaskNotMatched, "ProcessTaskNotMatched"},
{AsstMsg::ProcessTaskStopAction, "ProcessTaskStopAction"},
{AsstMsg::TextDetected, "TextDetected"},
{AsstMsg::RecruitTagsDetected, "RecruitTagsDetected"},
{AsstMsg::OcrResultError, "OcrResultError"},
{AsstMsg::RecruitSpecialTag, "RecruitSpecialTag"},
{AsstMsg::RecruitResult, "RecruitResult"},
{AsstMsg::AppendTask, "AppendTask"},
{AsstMsg::OpersDetected, "OpersDetected"},
{AsstMsg::OpersIdtfResult, "OpersIdtfResult"},
{AsstMsg::InfrastComb, "InfrastComb"},
{AsstMsg::EnterStation, "EnterStation"},
{AsstMsg::StationInfo, "StationInfo"},
{AsstMsg::ReadyToShift, "ReadyToShift"},
{AsstMsg::ShiftCompleted, "ShiftCompleted"},
{AsstMsg::NoNeedToShift, "NoNeedToShift"}
{ AsstMsg::PtrIsNull, "PtrIsNull" },
{ AsstMsg::ImageIsEmpty, "ImageIsEmpty" },
{ AsstMsg::WindowMinimized, "WindowMinimized" },
{ AsstMsg::InitFaild, "InitFaild" },
{ AsstMsg::TaskStart, "TaskStart" },
{ AsstMsg::ImageFindResult, "ImageFindResult" },
{ AsstMsg::ImageMatched, "ImageMatched" },
{ AsstMsg::StageDrops, "StageDrops" },
{ AsstMsg::TaskMatched, "TaskMatched" },
{ AsstMsg::ReachedLimit, "ReachedLimit" },
{ AsstMsg::ReadyToSleep, "ReadyToSleep" },
{ AsstMsg::EndOfSleep, "EndOfSleep" },
{ AsstMsg::AppendProcessTask, "AppendProcessTask" },
{ AsstMsg::TaskCompleted, "TaskCompleted" },
{ AsstMsg::TaskChainCompleted, "TaskChainCompleted" },
{ AsstMsg::PrintWindow, "PrintWindow" },
{ AsstMsg::TaskError, "TaskError" },
{ AsstMsg::ProcessTaskNotMatched, "ProcessTaskNotMatched" },
{ AsstMsg::ProcessTaskStopAction, "ProcessTaskStopAction" },
{ AsstMsg::TextDetected, "TextDetected" },
{ AsstMsg::RecruitTagsDetected, "RecruitTagsDetected" },
{ AsstMsg::OcrResultError, "OcrResultError" },
{ AsstMsg::RecruitSpecialTag, "RecruitSpecialTag" },
{ AsstMsg::RecruitResult, "RecruitResult" },
{ AsstMsg::AppendTask, "AppendTask" },
{ AsstMsg::InfrastSkillsDetected, "InfrastSkillsDetected" },
{ AsstMsg::InfrastSkillsResult, "InfrastSkillsResult" },
{ AsstMsg::InfrastComb, "InfrastComb" },
{ AsstMsg::EnterFacility, "EnterFacility" },
{ AsstMsg::FacilityInfo, "FacilityInfo" }
};
return os << _type_name.at(type);
}
enum UsesOfDrones {
enum UsesOfDrones
{
DronesNotUse = 0,
DronesTrade = 0x100,
DronesTradeMoney = DronesTrade & 0x10,
@@ -104,4 +101,4 @@ namespace asst {
// const json::value& 消息详情json每种消息不同Todo需要补充个协议文档啥的
// void* 外部调用者自定义参数,每次回调会带出去,建议传个(void*)this指针进来
using AsstCallback = std::function<void(AsstMsg, const json::value&, void*)>;
}
}

View File

@@ -1,12 +1,15 @@
#pragma once
#pragma once
#include <fstream>
#include <sstream>
#include <string>
#include <Windows.h>
namespace asst {
namespace utils {
namespace asst
{
namespace utils
{
static std::string get_cur_dir()
{
static std::string cur_dir;
@@ -37,14 +40,32 @@ namespace asst {
return str;
}
static std::vector<std::string> string_split(const std::string& str, const std::string& delimiter)
{
std::string::size_type pos1 = 0;
std::string::size_type pos2 = str.find(delimiter);
std::vector<std::string> result;
while (std::string::npos != pos2) {
result.emplace_back(str.substr(pos1, pos2 - pos1));
pos1 = pos2 + delimiter.size();
pos2 = str.find(delimiter, pos1);
}
if (pos1 != str.length())
result.emplace_back(str.substr(pos1));
return result;
}
static std::string get_format_time()
{
SYSTEMTIME curtime;
GetLocalTime(&curtime);
char buff[64] = { 0 };
sprintf_s(buff, "%04d-%02d-%02d %02d:%02d:%02d.%03d",
curtime.wYear, curtime.wMonth, curtime.wDay,
curtime.wHour, curtime.wMinute, curtime.wSecond, curtime.wMilliseconds);
curtime.wYear, curtime.wMonth, curtime.wDay,
curtime.wHour, curtime.wMinute, curtime.wSecond, curtime.wMilliseconds);
return buff;
}
@@ -60,8 +81,10 @@ namespace asst {
memset(str, 0, len + 1);
WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, len, NULL, NULL);
std::string strTemp = str;
if (wstr) delete[] wstr;
if (str) delete[] str;
if (wstr)
delete[] wstr;
if (str)
delete[] str;
return strTemp;
}
@@ -77,12 +100,14 @@ namespace asst {
memset(szGBK, 0, len + 1);
WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, szGBK, len, NULL, NULL);
std::string strTemp(szGBK);
if (wszGBK) delete[] wszGBK;
if (szGBK) delete[] szGBK;
if (wszGBK)
delete[] wszGBK;
if (szGBK)
delete[] szGBK;
return strTemp;
}
template<typename RetTy, typename ArgType>
template <typename RetTy, typename ArgType>
constexpr inline RetTy make_rect(const ArgType& rect)
{
return RetTy{ rect.x, rect.y, rect.width, rect.height };
@@ -104,10 +129,7 @@ namespace asst {
constexpr static uchar Bom_1 = 0xBB;
constexpr static uchar Bom_2 = 0xBF;
if (str.size() >= 3
&& static_cast<uchar>(str.at(0)) == Bom_0
&& static_cast<uchar>(str.at(1)) == Bom_1
&& static_cast<uchar>(str.at(2)) == Bom_2) {
if (str.size() >= 3 && static_cast<uchar>(str.at(0)) == Bom_0 && static_cast<uchar>(str.at(1)) == Bom_1 && static_cast<uchar>(str.at(2)) == Bom_2) {
str.assign(str.begin() + 3, str.end());
return str;
}
@@ -122,9 +144,7 @@ namespace asst {
hash2.insert(hash2.begin(), HammingFlags - hash2.size(), '0');
int dist = 0;
for (int i = 0; i < HammingFlags; i = i + 16) {
unsigned long long x
= strtoull(hash1.substr(i, 16).c_str(), NULL, 16)
^ strtoull(hash2.substr(i, 16).c_str(), NULL, 16);
unsigned long long x = strtoull(hash1.substr(i, 16).c_str(), NULL, 16) ^ strtoull(hash2.substr(i, 16).c_str(), NULL, 16);
while (x) {
dist++;
x = x & (x - 1);
@@ -174,4 +194,4 @@ namespace asst {
// return str;
//}
}
}
}

View File

@@ -1,12 +1,13 @@
#include "Controller.h"
#include "Controller.h"
#include <stdint.h>
#include <WinUser.h>
#include <stdint.h>
#include <vector>
#include <utility>
#include <algorithm>
#include <chrono>
#include <regex>
#include <utility>
#include <vector>
#include <opencv2/opencv.hpp>
@@ -15,9 +16,7 @@
#include "Resource.h"
#include "UserConfiger.h"
using namespace asst;
Controller::Controller()
asst::Controller::Controller()
: m_rand_engine(std::chrono::system_clock::now().time_since_epoch().count())
{
LogTraceFunction;
@@ -48,6 +47,61 @@ Controller::Controller()
m_cmd_thread = std::thread(bind_pipe_working_proc);
}
bool asst::Controller::connect_adb(const std::string& address)
{
LogTraceScope("connect_adb " + address);
std::string connect_cmd = utils::string_replace_all(
utils::string_replace_all(m_emulator_info.adb.connect, "[Adb]", m_adb_path),
"[Address]", address);
auto&& [connect_ret, connect_result] = call_command(connect_cmd);
// 端口即使错误命令仍然会返回0TODO 对connect_result进行判断
if (!connect_ret) {
return false;
}
std::string display_cmd = utils::string_replace_all(
utils::string_replace_all(m_emulator_info.adb.display, "[Adb]", m_adb_path),
"[Address]", address);
auto&& [display_ret, display_result] = call_command(display_cmd);
if (!display_ret) {
return false;
}
std::string display_pipe_str(
std::make_move_iterator(display_result.begin()),
std::make_move_iterator(display_result.end()));
int size_value1 = 0;
int size_value2 = 0;
sscanf_s(display_pipe_str.c_str(), m_emulator_info.adb.display_format.c_str(), &size_value1, &size_value2);
// 为了防止抓取句柄的时候手机是竖屏的(还没进游戏),这里取大的值为宽,小的为高
// 总不能有人竖屏玩明日方舟吧(?
m_emulator_info.adb.display_width = (std::max)(size_value1, size_value2);
m_emulator_info.adb.display_height = (std::min)(size_value1, size_value2);
constexpr double DefaultRatio =
static_cast<double>(WindowWidthDefault) / static_cast<double>(WindowHeightDefault);
double cur_ratio = static_cast<double>(m_emulator_info.adb.display_width) / static_cast<double>(m_emulator_info.adb.display_height);
if (cur_ratio >= DefaultRatio // 说明是宽屏或默认16:9按照高度计算缩放
|| std::fabs(cur_ratio - DefaultRatio) < DoubleDiff) {
int scale_width = cur_ratio * WindowHeightDefault;
m_scale_size = std::make_pair(scale_width, WindowHeightDefault);
m_control_scale = static_cast<double>(m_emulator_info.adb.display_height) / static_cast<double>(WindowHeightDefault);
}
else { // 否则可能是偏正方形的屏幕,按宽度计算
int scale_height = WindowWidthDefault / cur_ratio;
m_scale_size = std::make_pair(WindowWidthDefault, scale_height);
m_control_scale = static_cast<double>(m_emulator_info.adb.display_width) / static_cast<double>(WindowWidthDefault);
}
m_emulator_info.adb.click = utils::string_replace_all(utils::string_replace_all(m_emulator_info.adb.click, "[Adb]", m_adb_path), "[Address]", address);
m_emulator_info.adb.swipe = utils::string_replace_all(utils::string_replace_all(m_emulator_info.adb.swipe, "[Adb]", m_adb_path), "[Address]", address);
m_emulator_info.adb.screencap = utils::string_replace_all(utils::string_replace_all(m_emulator_info.adb.screencap, "[Adb]", m_adb_path), "[Address]", address);
return true;
}
asst::Controller::~Controller()
{
LogTraceFunction;
@@ -55,7 +109,7 @@ asst::Controller::~Controller()
m_thread_exit = true;
//m_thread_idle = true;
m_cmd_condvar.notify_all();
m_completed_id = UINT_MAX; // make all WinMacor::wait to exit
m_completed_id = UINT_MAX; // make all WinMacor::wait to exit
if (m_cmd_thread.joinable()) {
m_cmd_thread.join();
@@ -67,20 +121,53 @@ asst::Controller::~Controller()
::CloseHandle(m_pipe_child_write);
}
Rect asst::Controller::shaped_correct(const Rect& rect) const
asst::Rect asst::Controller::shaped_correct(const Rect& rect) const
{
if (rect.width == 0 || rect.height == 0) {
if (rect.empty()) {
return rect;
}
// 明日方舟在异形屏上,有的地方是按比例缩放的,有的地方又是直接位移。没法整,这里简单粗暴一点截一个长条
Rect dst = rect;
if (m_scale_size.first != GeneralConfiger::WindowWidthDefault) { // 说明是宽屏
dst.x = 0;
dst.width = m_scale_size.first - 1;
if (m_scale_size.first != WindowWidthDefault) { // 说明是宽屏
if (rect.width <= WindowWidthDefault / 2) {
if (rect.x + rect.width <= WindowWidthDefault / 2) { // 整个矩形都在左半边
dst.x = 0;
dst.width = m_scale_size.first / 2;
}
else if (rect.x >= WindowWidthDefault / 2) { // 整个矩形都在右半边
dst.x = m_scale_size.first / 2;
dst.width = m_scale_size.first / 2;
}
else { // 整个矩形横跨了中线
dst.x = 0;
dst.width = m_scale_size.first;
}
}
else {
dst.x = 0;
dst.width = m_scale_size.first;
}
}
else if (m_scale_size.second != GeneralConfiger::WindowHeightDefault) { // 说明是偏方形屏
dst.y = 0;
dst.height = m_scale_size.second - 1;
else if (m_scale_size.second != WindowHeightDefault) { // 说明是偏方形屏
if (rect.height <= WindowHeightDefault / 2) {
if (rect.y + rect.height <= WindowHeightDefault / 2) { // 整个矩形都在上半边
dst.y = 0;
dst.height = m_scale_size.second / 2;
}
else if (rect.y >= WindowHeightDefault / 2) { // 整个矩形都在下半边
dst.y = m_scale_size.second / 2;
dst.height = m_scale_size.second / 2; // 整个矩形横跨了中线
}
else {
dst.y = 0;
dst.height = m_scale_size.second;
}
}
else {
dst.y = 0;
dst.height = m_scale_size.second;
}
}
return dst;
}
@@ -92,7 +179,7 @@ void asst::Controller::pipe_working_proc()
while (!m_thread_exit) {
std::unique_lock<std::mutex> cmd_queue_lock(m_cmd_queue_mutex);
if (!m_cmd_queue.empty()) { // 队列中有任务就执行任务
if (!m_cmd_queue.empty()) { // 队列中有任务就执行任务
std::string cmd = m_cmd_queue.front();
m_cmd_queue.pop();
cmd_queue_lock.unlock();
@@ -119,15 +206,14 @@ void asst::Controller::pipe_working_proc()
}
}
bool Controller::try_capture(const EmulatorInfo& info, bool without_handle)
bool asst::Controller::try_capture(const EmulatorInfo& info, bool without_handle)
{
LogTraceFunction;
LogTraceScope("try_capture | " + info.name);
const HandleInfo& handle_info = info.handle;
std::string adb_dir;
if (!without_handle) { // 使用模拟器自带的adb
// 转成宽字符的
if (!without_handle) { // 使用模拟器自带的adb
// 转成宽字符的
wchar_t* class_wbuff = nullptr;
if (!handle_info.class_name.empty()) {
size_t class_len = (handle_info.class_name.size() + 1) * 2;
@@ -189,94 +275,48 @@ bool Controller::try_capture(const EmulatorInfo& info, bool without_handle)
m_handle = window_handle;
log.trace("Handle:", m_handle, "Name:", m_emulator_info.name);
adb_dir = emulator_path.substr(0, emulator_path.find_last_of('\\') + 1);
adb_dir = '"' + utils::string_replace_all(m_emulator_info.adb.path, "[EmulatorPath]", adb_dir) + '"';
adb_dir = utils::string_replace_all(adb_dir, "[ExecDir]", utils::get_cur_dir());
std::string adb_path = emulator_path.substr(0, emulator_path.find_last_of('\\') + 1);
adb_path = '"' + utils::string_replace_all(m_emulator_info.adb.path, "[EmulatorPath]", adb_path) + '"';
adb_path = utils::string_replace_all(adb_path, "[ExecDir]", utils::get_cur_dir());
m_adb_path = std::move(adb_path);
}
else { // 使用辅助自带的标准adb
else { // 使用辅助自带的标准adb
m_emulator_info = info;
adb_dir = '"' + utils::string_replace_all(m_emulator_info.adb.path, "[ExecDir]", utils::get_cur_dir()) + '"';
m_adb_path = '"' + utils::string_replace_all(m_emulator_info.adb.path, "[ExecDir]", utils::get_cur_dir()) + '"';
}
//// 针对雷电模拟器连接问题的专用补丁
//if (m_emulator_info.handle.class_name == "LDPlayerMainFrame") {
// log.trace("LDPlayer Connection Patch Activated.");
// std::string connect_cmd = utils::string_replace_all("[Adb] devices", "[Adb]", adb_dir);
// auto devices_result = call_command(connect_cmd);
// std::string devices_list(
// std::make_move_iterator(devices_result.begin()),
// std::make_move_iterator(devices_result.end()));
// // 查找连接的所有设备
// std::string device = {};
// 优先使用addresses里指定的地址
for (const std::string& address : info.adb.addresses) {
if (connect_adb(address)) {
return true;
}
}
// if (devices_list.find("emulator") != devices_list.npos) {
// device = devices_list.substr(devices_list.find("emulator"), 13);
// m_emulator_info.adb.connect = "";
// }
// else if (devices_list.find("127") != devices_list.npos) {
// device = devices_list.substr(devices_list.find("127"), 14);
// }
// 若指定地址都没连上再尝试用devices查找地址
std::string devices_cmd = utils::string_replace_all(m_emulator_info.adb.devices, "[Adb]", m_adb_path);
auto&& [devices_ret, devices_result] = call_command(devices_cmd);
if (!devices_ret) {
return false;
}
std::string devices_pipe_str(
std::make_move_iterator(devices_result.begin()),
std::make_move_iterator(devices_result.end()));
auto lines = utils::string_split(devices_pipe_str, "\r\n");
// if (device.empty()) {
// log.trace("no device detected.");
// return false;
// }
// log.trace("device detected: ", device);
// m_emulator_info.adb.connect = utils::string_replace_all(m_emulator_info.adb.connect, "127.0.0.1:5555", device);
// m_emulator_info.adb.click = utils::string_replace_all(m_emulator_info.adb.click, "-e", "-s " + device);
// m_emulator_info.adb.swipe = utils::string_replace_all(m_emulator_info.adb.swipe, "-e", "-s " + device);
// m_emulator_info.adb.display = utils::string_replace_all(m_emulator_info.adb.display, "-e", "-s " + device);
// m_emulator_info.adb.screencap = utils::string_replace_all(m_emulator_info.adb.screencap, "-e", "-s " + device);
//}
std::string connect_cmd = utils::string_replace_all(m_emulator_info.adb.connect, "[Adb]", adb_dir);
auto&& [connect_ret, connect_result] = call_command(connect_cmd);
// 端口即使错误命令仍然会返回0TODO 对connect_result进行判断
if (!connect_ret) {
std::string address;
const std::regex address_regex(m_emulator_info.adb.address_regex);
for (const std::string& line : lines) {
std::smatch smatch;
if (std::regex_match(line, smatch, address_regex)) {
address = smatch[1];
}
}
log.trace("device address", address);
if (address.empty()) {
return false;
}
std::string display_cmd = utils::string_replace_all(m_emulator_info.adb.display, "[Adb]", adb_dir);
auto&& [display_ret, display_result] = call_command(display_cmd);
if (!display_ret) {
return false;
}
std::string display_pipe_str(
std::make_move_iterator(display_result.begin()),
std::make_move_iterator(display_result.end()));
int size_value1 = 0;
int size_value2 = 0;
sscanf_s(display_pipe_str.c_str(), m_emulator_info.adb.display_regex.c_str(), &size_value1, &size_value2);
// 为了防止抓取句柄的时候手机是竖屏的(还没进游戏),这里取大的值为宽,小的为高
// 总不能有人竖屏玩明日方舟吧(?
m_emulator_info.adb.display_width = (std::max)(size_value1, size_value2);
m_emulator_info.adb.display_height = (std::min)(size_value1, size_value2);
constexpr double DefaultRatio =
static_cast<double>(GeneralConfiger::WindowWidthDefault) / static_cast<double>(GeneralConfiger::WindowHeightDefault);
double cur_ratio = static_cast<double>(m_emulator_info.adb.display_width) / static_cast<double>(m_emulator_info.adb.display_height);
if (cur_ratio >= DefaultRatio // 说明是宽屏或默认16:9按照高度计算缩放
|| std::fabs(cur_ratio - DefaultRatio) < DoubleDiff)
{
int scale_width = cur_ratio * GeneralConfiger::WindowHeightDefault;
m_scale_size = std::make_pair(scale_width, GeneralConfiger::WindowHeightDefault);
m_control_scale = static_cast<double>(m_emulator_info.adb.display_height) / static_cast<double>(GeneralConfiger::WindowHeightDefault);
}
else
{ // 否则可能是偏正方形的屏幕,按宽度计算
int scale_height = GeneralConfiger::WindowWidthDefault / cur_ratio;
m_scale_size = std::make_pair(GeneralConfiger::WindowWidthDefault, scale_height);
m_control_scale = static_cast<double>(m_emulator_info.adb.display_width) / static_cast<double>(GeneralConfiger::WindowWidthDefault);
}
m_emulator_info.adb.click = utils::string_replace_all(m_emulator_info.adb.click, "[Adb]", adb_dir);
m_emulator_info.adb.swipe = utils::string_replace_all(m_emulator_info.adb.swipe, "[Adb]", adb_dir);
m_emulator_info.adb.screencap = utils::string_replace_all(m_emulator_info.adb.screencap, "[Adb]", adb_dir);
return true;
return connect_adb(address);
}
//void asst::Controller::set_idle(bool flag)
@@ -292,14 +332,14 @@ bool Controller::try_capture(const EmulatorInfo& info, bool without_handle)
// }
//}
std::pair<bool, std::vector<unsigned char>> Controller::call_command(const std::string& cmd)
std::pair<bool, std::vector<unsigned char>> asst::Controller::call_command(const std::string& cmd)
{
LogTraceFunction;
static std::mutex pipe_mutex;
std::unique_lock<std::mutex> pipe_lock(pipe_mutex);
PROCESS_INFORMATION process_info = { 0 }; // 进程信息结构体
PROCESS_INFORMATION process_info = { 0 }; // 进程信息结构体
::CreateProcessA(NULL, const_cast<LPSTR>(cmd.c_str()), NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &m_child_startup_info, &process_info);
std::vector<uchar> pipe_data;
@@ -368,7 +408,7 @@ void asst::Controller::convert_lf(std::vector<unsigned char>& data)
data.erase(next_iter, data.end());
}
Point asst::Controller::rand_point_in_rect(const Rect& rect)
asst::Point asst::Controller::rand_point_in_rect(const Rect& rect)
{
int x = 0, y = 0;
if (rect.width == 0) {
@@ -440,7 +480,7 @@ bool asst::Controller::screencap()
//m_cache_image = std::move(temp_image);
}
int Controller::click(const Point& p, bool block)
int asst::Controller::click(const Point& p, bool block)
{
int x = p.x * m_control_scale;
int y = p.y * m_control_scale;
@@ -449,15 +489,14 @@ int Controller::click(const Point& p, bool block)
return click_without_scale(Point(x, y), block);
}
int Controller::click(const Rect& rect, bool block)
int asst::Controller::click(const Rect& rect, bool block)
{
return click(rand_point_in_rect(rect), block);
}
int asst::Controller::click_without_scale(const Point& p, bool block)
{
if (p.x < 0 || p.x >= m_emulator_info.adb.display_width
|| p.y < 0 || p.y >= m_emulator_info.adb.display_height) {
if (p.x < 0 || p.x >= m_emulator_info.adb.display_width || p.y < 0 || p.y >= m_emulator_info.adb.display_height) {
log.error("click point out of range");
}
std::string cur_cmd = utils::string_replace_all(m_emulator_info.adb.click, "[x]", std::to_string(p.x));
@@ -492,10 +531,7 @@ int asst::Controller::swipe(const Rect& r1, const Rect& r2, int duration, bool b
int asst::Controller::swipe_without_scale(const Point& p1, const Point& p2, int duration, bool block, int extra_delay, bool extra_swipe)
{
if (p1.x < 0 || p1.x >= m_emulator_info.adb.display_width
|| p1.y < 0 || p1.y >= m_emulator_info.adb.display_height
|| p2.x < 0 || p2.x >= m_emulator_info.adb.display_width
|| p2.y < 0 || p2.y >= m_emulator_info.adb.display_height) {
if (p1.x < 0 || p1.x >= m_emulator_info.adb.display_width || p1.y < 0 || p1.y >= m_emulator_info.adb.display_height || p2.x < 0 || p2.x >= m_emulator_info.adb.display_width || p2.y < 0 || p2.y >= m_emulator_info.adb.display_height) {
log.error("swipe point out of range");
}
std::string cur_cmd = utils::string_replace_all(m_emulator_info.adb.swipe, "[x1]", std::to_string(p1.x));
@@ -511,7 +547,7 @@ int asst::Controller::swipe_without_scale(const Point& p1, const Point& p2, int
int id = 0;
int extra_swipe_dist = resource.cfg().get_options().adb_extra_swipe_dist * m_control_scale;
int extra_swipe_dist = resource.cfg().get_options().adb_extra_swipe_dist /* * m_control_scale*/;
int extra_swipe_duration = resource.cfg().get_options().adb_extra_swipe_duration;
// 额外的滑动adb有bug同样的参数偶尔会划得非常远。额外做一个短程滑动把之前的停下来
@@ -542,7 +578,7 @@ int asst::Controller::swipe_without_scale(const Point& p1, const Point& p2, int
if (block) {
wait(id);
std::this_thread::sleep_for(std::chrono::microseconds(extra_delay));
std::this_thread::sleep_for(std::chrono::milliseconds(extra_delay));
}
return id;
}
@@ -554,9 +590,15 @@ int asst::Controller::swipe_without_scale(const Rect& r1, const Rect& r2, int du
cv::Mat asst::Controller::get_image(bool raw)
{
static bool has_successful = false;
if (!screencap()) {
return cv::Mat();
// 如果之前成功截图过,这次没成功可能是一次意外,重试一次
// 点名批评雷电模拟器!
if (!(has_successful && screencap())) {
return cv::Mat();
}
}
has_successful = true;
//std::shared_lock<std::shared_mutex> image_lock(m_image_mutex);
if (raw) {
return m_cache_image;
@@ -570,4 +612,4 @@ cv::Mat asst::Controller::get_image(bool raw)
cv::resize(m_cache_image, resize_mat, size, cv::INPAINT_NS);
return resize_mat;
}
}
}

View File

@@ -1,15 +1,15 @@
#pragma once
#pragma once
#include <string>
#include <random>
#include <optional>
#include <random>
#include <string>
#include <memory> // for pimpl
#include <thread>
#include <mutex>
#include <shared_mutex>
#include <queue>
#include <condition_variable>
#include <memory> // for pimpl
#include <mutex>
#include <queue>
#include <shared_mutex>
#include <thread>
#include <Windows.h>
@@ -17,7 +17,8 @@
#include "AsstDef.h"
namespace asst {
namespace asst
{
class Controller
{
public:
@@ -25,7 +26,8 @@ namespace asst {
Controller(Controller&&) = delete;
~Controller();
static Controller& get_instance() {
static Controller& get_instance()
{
static Controller unique_instance;
return unique_instance;
}
@@ -52,9 +54,17 @@ namespace asst {
Controller& operator=(const Controller&) = delete;
Controller& operator=(Controller&&) = delete;
std::pair<int, int> get_scale_size() const noexcept
{
return m_scale_size;
}
private:
Controller();
bool connect_adb(const std::string& address);
void pipe_working_proc();
std::pair<bool, std::vector<unsigned char>> call_command(const std::string& cmd);
int push_cmd(const std::string& cmd);
@@ -71,21 +81,22 @@ namespace asst {
std::condition_variable m_cmd_condvar;
std::queue<std::string> m_cmd_queue;
std::atomic<unsigned> m_completed_id = 0;
unsigned m_push_id = 0; // push_id的自增总是伴随着queue的push肯定是要上锁的所以没必要原子
unsigned m_push_id = 0; // push_id的自增总是伴随着queue的push肯定是要上锁的所以没必要原子
//std::shared_mutex m_image_mutex;
cv::Mat m_cache_image;
bool m_image_convert_lf = false;
constexpr static int PipeBuffSize = 1048576; // 管道缓冲区大小
HANDLE m_pipe_read = nullptr; // 读管道句柄
HANDLE m_pipe_write = nullptr; // 写管道句柄
HANDLE m_pipe_child_read = nullptr; // 子进程的读管道句柄
HANDLE m_pipe_child_write = nullptr; // 子进程的写管道句柄
SECURITY_ATTRIBUTES m_pipe_sec_attr = { 0 }; // 管道安全描述符
STARTUPINFOA m_child_startup_info = { 0 }; // 子进程启动信息
constexpr static int PipeBuffSize = 1048576; // 管道缓冲区大小
HANDLE m_pipe_read = nullptr; // 读管道句柄
HANDLE m_pipe_write = nullptr; // 写管道句柄
HANDLE m_pipe_child_read = nullptr; // 子进程的读管道句柄
HANDLE m_pipe_child_write = nullptr; // 子进程的写管道句柄
SECURITY_ATTRIBUTES m_pipe_sec_attr = { 0 }; // 管道安全描述符
STARTUPINFOA m_child_startup_info = { 0 }; // 子进程启动信息
EmulatorInfo m_emulator_info;
std::string m_adb_path;
HWND m_handle = nullptr;
std::minstd_rand m_rand_engine;
std::pair<int, int> m_scale_size;

View File

@@ -1,8 +1,8 @@
#include "CreditShopImageAnalyzer.h"
#include "CreditShopImageAnalyzer.h"
#include "MatchImageAnalyzer.h"
#include "MultiMatchImageAnalyzer.h"
#include "OcrImageAnalyzer.h"
#include "MatchImageAnalyzer.h"
#include "Resource.h"
#include "AsstUtils.hpp"
@@ -13,9 +13,7 @@ bool asst::CreditShopImageAnalyzer::analyze()
m_need_to_buy.clear();
m_result.clear();
return commoditys_analyze()
&& whether_to_buy_analyze()
&& sold_out_analyze();
return commoditys_analyze() && whether_to_buy_analyze() && sold_out_analyze();
}
bool asst::CreditShopImageAnalyzer::commoditys_analyze()
@@ -71,7 +69,7 @@ bool asst::CreditShopImageAnalyzer::whether_to_buy_analyze()
}
const auto& ocr_res = ocr_analyzer.get_result();
#ifdef LOG_TRACE
#ifdef LOG_TRACE
cv::rectangle(m_image_draw, utils::make_rect<cv::Rect>(commodity), cv::Scalar(0, 0, 255), 2);
#endif
m_need_to_buy.emplace_back(commodity);
@@ -94,7 +92,7 @@ bool asst::CreditShopImageAnalyzer::sold_out_analyze()
for (const Rect& commodity : m_need_to_buy) {
sold_out_analyzer.set_roi(commodity);
if (sold_out_analyzer.analyze()) {
#ifdef LOG_TRACE
#ifdef LOG_TRACE
cv::rectangle(m_image_draw, utils::make_rect<cv::Rect>(commodity), cv::Scalar(0, 0, 255));
cv::putText(m_image_draw, "Sold Out", cv::Point(commodity.x, commodity.y), 1, 2, cv::Scalar(255, 0, 0));
#endif // LOG_TRACE
@@ -106,4 +104,4 @@ bool asst::CreditShopImageAnalyzer::sold_out_analyze()
}
return !m_result.empty();
}
}

View File

@@ -1,7 +1,8 @@
#pragma once
#pragma once
#include "AbstractImageAnalyzer.h"
namespace asst {
namespace asst
{
class CreditShopImageAnalyzer final : public AbstractImageAnalyzer
{
public:
@@ -11,15 +12,19 @@ namespace asst {
virtual bool analyze() override;
const std::vector<Rect>& get_result() const noexcept {
const std::vector<Rect>& get_result() const noexcept
{
return m_result;
}
private:
// 该分析器不支持外部设置ROI
virtual void set_roi(const Rect& roi) noexcept override {
virtual void set_roi(const Rect& roi) noexcept override
{
AbstractImageAnalyzer::set_roi(roi);
}
virtual void set_image(const cv::Mat& image, const Rect& roi) {
virtual void set_image(const cv::Mat& image, const Rect& roi)
{
AbstractImageAnalyzer::set_image(image, roi);
}
bool commoditys_analyze();

View File

@@ -1,7 +1,7 @@
#include "CreditShoppingTask.h"
#include "CreditShoppingTask.h"
#include <vector>
#include <algorithm>
#include <vector>
#include <opencv2/opencv.hpp>
@@ -14,8 +14,8 @@
bool asst::CreditShoppingTask::run()
{
json::value task_start_json = json::object{
{ "task_type", "CreditShoppingTask" },
{ "task_chain", m_task_chain}
{ "task_type", "CreditShoppingTask" },
{ "task_chain", m_task_chain }
};
m_callback(AsstMsg::TaskStart, task_start_json, m_callback_arg);
@@ -83,4 +83,4 @@ bool asst::CreditShoppingTask::run()
}
return true;
}
}

View File

@@ -1,4 +1,4 @@
#pragma once
#pragma once
#include "AbstractTask.h"
@@ -6,13 +6,15 @@
#include "AsstDef.h"
namespace asst {
namespace asst
{
class CreditShoppingTask : public AbstractTask
{
public:
using AbstractTask::AbstractTask;
virtual ~CreditShoppingTask() = default;
virtual bool run() override;
private:
};
}
}

View File

@@ -1,4 +1,4 @@
#include "GeneralConfiger.h"
#include "GeneralConfiger.h"
#include <json.h>
@@ -9,9 +9,7 @@ bool asst::GeneralConfiger::parse(const json::value& json)
const json::value& options_json = json.at("options");
{
m_options.connect_type = static_cast<ConnectType>(options_json.at("connectType").as_integer());
m_options.connect_remote_address = options_json.at("connectRemoteAddress").as_string();
m_options.task_delay = options_json.at("taskDelay").as_integer();
//m_options.identify_cache = options_json.at("identifyCache").as_boolean();
m_options.control_delay_lower = options_json.at("controlDelayRange")[0].as_integer();
m_options.control_delay_upper = options_json.at("controlDelayRange")[1].as_integer();
m_options.print_window = options_json.at("printWindow").as_boolean();
@@ -36,18 +34,22 @@ bool asst::GeneralConfiger::parse(const json::value& json)
const json::object& adb_json = emulator_json.at("adb").as_object();
emulator_info.adb.path = adb_json.at("path").as_string();
for (const json::value& address_json : adb_json.at("addresses").as_array()) {
emulator_info.adb.addresses.emplace_back(address_json.as_string());
}
emulator_info.adb.devices = adb_json.at("devices").as_string();
emulator_info.adb.address_regex = adb_json.at("addressRegex").as_string();
emulator_info.adb.connect = adb_json.at("connect").as_string();
emulator_info.adb.click = adb_json.at("click").as_string();
emulator_info.adb.swipe = adb_json.at("swipe").as_string();
emulator_info.adb.display = adb_json.at("display").as_string();
emulator_info.adb.display_regex = adb_json.at("displayRegex").as_string();
emulator_info.adb.display_format = adb_json.at("displayFormat").as_string();
emulator_info.adb.screencap = adb_json.at("screencap").as_string();
//emulator_info.adb.pullscreen = adb_json.at("pullscreen").as_string();
emulator_info.path = emulator_json.get("path", std::string());
m_emulators_info.emplace(name, std::move(emulator_info));
}
return true;
}
}

View File

@@ -1,34 +1,35 @@
#pragma once
#pragma once
#include "AbstractConfiger.h"
#include <memory>
#include <string>
#include <unordered_map>
#include <memory>
#include "AsstDef.h"
namespace asst {
enum class ConnectType {
namespace asst
{
enum class ConnectType
{
Emulator,
USB,
Remote
Custom
};
struct Options {
ConnectType connect_type; // 连接类型
std::string connect_remote_address; // 连接局域网中的安卓设备的地址仅在connectType为Remote时生效
int task_delay = 0; // 任务间延时越快操作越快但会增加CPU消耗
int control_delay_lower = 0; // 点击随机延时下限:每次点击操作会进行随机延时
int control_delay_upper = 0; // 点击随机延时上限:每次点击操作会进行随机延时
bool print_window = false; // 截图功能开启后每次结算界面会截图到screenshot目录下
bool penguin_report = false; // 企鹅数据汇报:每次到结算界面,是否汇报掉落数据至企鹅数据 https://penguin-stats.cn/
std::string penguin_report_cmd_line;// 企鹅数据汇报的命令
std::string penguin_report_server; // 企鹅数据汇报接口"server"字段,"CN", "US", "JP" and "KR".
int ocr_gpu_index = -1; // OcrLite使用GPU编号-1(使用CPU)/0(使用GPU0)/1(使用GPU1)/...
int ocr_thread_number = 0; // OcrLite线程数量
int adb_extra_swipe_dist = 0; // 额外的滑动距离adb有bug同样的参数偶尔会划得非常远。额外做一个短程滑动把之前的停下来
int adb_extra_swipe_duration = -1; // 额外的滑动持续时间adb有bug同样的参数偶尔会划得非常远。额外做一个短程滑动把之前的停下来。若小于0则关闭额外滑动功能
struct Options
{
ConnectType connect_type; // 连接类型
int task_delay = 0; // 任务间延时越快操作越快但会增加CPU消耗
int control_delay_lower = 0; // 点击随机延时下限:每次点击操作会进行随机延时
int control_delay_upper = 0; // 点击随机延时上限:每次点击操作会进行随机延时
bool print_window = false; // 截图功能开启后每次结算界面会截图到screenshot目录下
bool penguin_report = false; // 企鹅数据汇报:每次到结算界面,是否汇报掉落数据至企鹅数据 https://penguin-stats.cn/
std::string penguin_report_cmd_line; // 企鹅数据汇报的命令
std::string penguin_report_server; // 企鹅数据汇报接口"server"字段,"CN", "US", "JP" and "KR".
int ocr_gpu_index = -1; // OcrLite使用GPU编号-1(使用CPU)/0(使用GPU0)/1(使用GPU1)/...
int ocr_thread_number = 0; // OcrLite线程数量
int adb_extra_swipe_dist = 0; // 额外的滑动距离adb有bug同样的参数偶尔会划得非常远。额外做一个短程滑动把之前的停下来
int adb_extra_swipe_duration = -1; // 额外的滑动持续时间adb有bug同样的参数偶尔会划得非常远。额外做一个短程滑动把之前的停下来。若小于0则关闭额外滑动功能
};
class GeneralConfiger : public AbstractConfiger
@@ -36,9 +37,6 @@ namespace asst {
public:
virtual ~GeneralConfiger() = default;
constexpr static int WindowWidthDefault = 1280;
constexpr static int WindowHeightDefault = 720;
const std::string& get_version() const noexcept
{
return m_version;

View File

@@ -1,22 +1,52 @@
#include "InfrastAbstractTask.h"
#include "InfrastAbstractTask.h"
#include "AsstMsg.h"
#include "Controller.h"
#include "InfrastFacilityImageAnalyzer.h"
#include "Logger.hpp"
#include "MatchImageAnalyzer.h"
#include "OcrImageAnalyzer.h"
#include "InfrastFacilityImageAnalyzer.h"
#include "Controller.h"
#include "Resource.h"
void asst::InfrastAbstractTask::set_work_mode(InfrastWorkMode work_mode) noexcept
{
m_work_mode = work_mode;
switch (work_mode) {
case InfrastWorkMode::Gentle:
m_work_mode_name = "Gentle";
break;
case InfrastWorkMode::Aggressive:
m_work_mode_name = "Aggressive";
break;
case InfrastWorkMode::Extreme:
m_work_mode_name = "Extreme";
break;
default:
m_work_mode_name.clear();
break;
}
}
bool asst::InfrastAbstractTask::enter_facility(const std::string& facility, int index)
{
LogTraceFunction;
json::value enter_json = json::object{
{ "facility", facility },
{ "index", index }
};
m_callback(AsstMsg::EnterFacility, enter_json, m_callback_arg);
const auto& image = ctrler.get_image();
InfrastFacilityImageAnalyzer analyzer(image);
analyzer.set_to_be_analyzed({ facility });
if (!analyzer.analyze()) {
log.trace("result is empty");
return false;
}
Rect rect = analyzer.get_rect(facility, index);
if (rect.empty()) {
log.trace("facility index is out of range");
return false;
}
@@ -30,6 +60,8 @@ bool asst::InfrastAbstractTask::enter_facility(const std::string& facility, int
bool asst::InfrastAbstractTask::enter_oper_list_page()
{
LogTraceFunction;
auto image = ctrler.get_image();
// 识别右边的“进驻”按钮
@@ -40,25 +72,30 @@ bool asst::InfrastAbstractTask::enter_oper_list_page()
// 如果没找到,说明“进驻信息”这个按钮没有被点开,那就点开它
if (!enter_analyzer.analyze()) {
log.trace("ready to analyze the stationed info button");
OcrImageAnalyzer station_analyzer(image);
const auto stationedinfo_task_ptr = std::dynamic_pointer_cast<OcrTaskInfo>(
resource.task().task_ptr("InfrastStationedInfo"));
station_analyzer.set_task_info(*stationedinfo_task_ptr);
if (station_analyzer.analyze()) {
log.trace("the stationed info button found");
ctrler.click(station_analyzer.get_result().front().rect);
sleep(stationedinfo_task_ptr->rear_delay);
// 点开了按钮之后,再识别一次右边的
image = ctrler.get_image();
enter_analyzer.set_image(image);
if (!enter_analyzer.analyze()) {
log.error("no enterlist button");
return false;
}
}
else {
log.error("no stationed info button");
return false;
}
}
log.trace("ready to click the enterlist button");
ctrler.click(enter_analyzer.get_result().front().rect);
sleep(enter_task_ptr->rear_delay);
@@ -67,6 +104,7 @@ bool asst::InfrastAbstractTask::enter_oper_list_page()
void asst::InfrastAbstractTask::async_swipe_of_operlist(bool reverse)
{
LogTraceFunction;
static Rect begin_rect = resource.task().task_ptr("InfrastOperListSwipeBegin")->specific_rect;
static Rect end_rect = resource.task().task_ptr("InfrastOperListSwipeEnd")->specific_rect;
static int duration = resource.task().task_ptr("InfrastOperListSwipeBegin")->pre_delay;
@@ -81,6 +119,7 @@ void asst::InfrastAbstractTask::async_swipe_of_operlist(bool reverse)
void asst::InfrastAbstractTask::await_swipe()
{
LogTraceFunction;
static int extra_delay = resource.task().task_ptr("InfrastOperListSwipeBegin")->rear_delay;
ctrler.wait(m_last_swipe_id);
@@ -89,6 +128,7 @@ void asst::InfrastAbstractTask::await_swipe()
bool asst::InfrastAbstractTask::click_bottomleft_tab()
{
LogTraceFunction;
const auto task_ptr = resource.task().task_ptr("InfrastBottomLeftTab");
ctrler.click(task_ptr->specific_rect);
sleep(task_ptr->rear_delay);
@@ -97,6 +137,7 @@ bool asst::InfrastAbstractTask::click_bottomleft_tab()
bool asst::InfrastAbstractTask::click_clear_button()
{
LogTraceFunction;
const auto task_ptr = resource.task().task_ptr("InfrastClearButton");
ctrler.click(task_ptr->specific_rect);
sleep(task_ptr->rear_delay);
@@ -105,6 +146,7 @@ bool asst::InfrastAbstractTask::click_clear_button()
bool asst::InfrastAbstractTask::click_confirm_button()
{
LogTraceFunction;
const auto task_ptr = std::dynamic_pointer_cast<OcrTaskInfo>(
resource.task().task_ptr("InfrastConfirmButton"));
ctrler.click(task_ptr->specific_rect);
@@ -114,6 +156,9 @@ bool asst::InfrastAbstractTask::click_confirm_button()
OcrImageAnalyzer analyzer;
analyzer.set_task_info(*task_ptr);
for (int i = 0; i != m_retry_times; ++i) {
if (need_exit()) {
return false;
}
const auto& image = ctrler.get_image();
analyzer.set_image(image);
if (!analyzer.analyze()) {
@@ -133,6 +178,7 @@ void asst::InfrastAbstractTask::sync_swipe_of_operlist(bool reverse)
void asst::InfrastAbstractTask::swipe_to_the_left_of_operlist()
{
LogTraceFunction;
static Rect begin_rect = resource.task().task_ptr("InfrastOperListSwipeToTheLeftBegin")->specific_rect;
static Rect end_rect = resource.task().task_ptr("InfrastOperListSwipeToTheLeftEnd")->specific_rect;
static int duration = resource.task().task_ptr("InfrastOperListSwipeToTheLeftBegin")->pre_delay;
@@ -140,6 +186,9 @@ void asst::InfrastAbstractTask::swipe_to_the_left_of_operlist()
static int loop_times = resource.task().task_ptr("InfrastOperListSwipeToTheLeftBegin")->max_times;
for (int i = 0; i != loop_times; ++i) {
if (need_exit()) {
return;
}
ctrler.swipe(end_rect, begin_rect, duration, true, 0, false);
}
sleep(extra_delay);
@@ -147,6 +196,7 @@ void asst::InfrastAbstractTask::swipe_to_the_left_of_operlist()
void asst::InfrastAbstractTask::swipe_to_the_left_of_main_ui()
{
LogTraceFunction;
static Rect begin_rect = resource.task().task_ptr("InfrastOperListSwipeToTheLeftBegin")->specific_rect;
static Rect end_rect = resource.task().task_ptr("InfrastOperListSwipeToTheLeftEnd")->specific_rect;
static int duration = resource.task().task_ptr("InfrastOperListSwipeToTheLeftBegin")->pre_delay;
@@ -157,10 +207,11 @@ void asst::InfrastAbstractTask::swipe_to_the_left_of_main_ui()
void asst::InfrastAbstractTask::swipe_to_the_right_of_main_ui()
{
LogTraceFunction;
static Rect begin_rect = resource.task().task_ptr("InfrastOperListSwipeToTheLeftBegin")->specific_rect;
static Rect end_rect = resource.task().task_ptr("InfrastOperListSwipeToTheLeftEnd")->specific_rect;
static int duration = resource.task().task_ptr("InfrastOperListSwipeToTheLeftBegin")->pre_delay;
static int extra_delay = resource.task().task_ptr("InfrastOperListSwipeToTheLeftBegin")->rear_delay;
ctrler.swipe(begin_rect, end_rect, duration, true, extra_delay, false);
}
}

View File

@@ -1,27 +1,33 @@
#pragma once
#pragma once
#include "AbstractTask.h"
#include "AsstDef.h"
namespace asst {
namespace asst
{
class InfrastAbstractTask : public AbstractTask
{
public:
using AbstractTask::AbstractTask;
virtual ~InfrastAbstractTask() = default;
virtual void set_work_mode(InfrastWorkMode work_mode) noexcept;
protected:
virtual bool enter_facility(const std::string& facility, int index = 0);
virtual bool enter_oper_list_page(); // 从刚点进基建的界面,到干员列表页
virtual bool enter_oper_list_page(); // 从刚点进基建的界面,到干员列表页
virtual void swipe_to_the_left_of_operlist(); // 滑动到干员列表的最左侧
virtual void swipe_to_the_left_of_main_ui(); // 滑动基建的主界面到最左侧
virtual void swipe_to_the_right_of_main_ui(); // 滑动基建的主界面到最右侧
virtual void swipe_to_the_left_of_operlist(); // 滑动到干员列表的最左侧
virtual void swipe_to_the_left_of_main_ui(); // 滑动基建的主界面到最左侧
virtual void swipe_to_the_right_of_main_ui(); // 滑动基建的主界面到最右侧
virtual void sync_swipe_of_operlist(bool reverse = false);
virtual void async_swipe_of_operlist(bool reverse = false);
virtual void await_swipe();
virtual bool click_bottomleft_tab(); // 点击进入设施后左下角的tab我也不知道这玩意该叫啥
virtual bool click_clear_button(); // 点击干员选择页面的“清空选择”按钮
virtual bool click_confirm_button(); // 点击干员选择页面的“确认”按钮
virtual bool click_bottomleft_tab(); // 点击进入设施后左下角的tab我也不知道这玩意该叫啥
virtual bool click_clear_button(); // 点击干员选择页面的“清空选择”按钮
virtual bool click_confirm_button(); // 点击干员选择页面的“确认”按钮
int m_last_swipe_id = 0;
InfrastWorkMode m_work_mode = InfrastWorkMode::Gentle;
std::string m_work_mode_name = "Gentle";
};
}
}

View File

@@ -1,4 +1,4 @@
#include "InfrastClueImageAnalyzer.h"
#include "InfrastClueImageAnalyzer.h"
#include "MultiMatchImageAnalyzer.h"
#include "Resource.h"
@@ -19,7 +19,7 @@ bool asst::InfrastClueImageAnalyzer::clue_detect()
const auto task_ptr = std::dynamic_pointer_cast<MatchTaskInfo>(
resource.task().task_ptr("InfrastClue"));
clue_analyzer.set_task_info(*task_ptr);
clue_analyzer.set_roi(m_roi); // 改任务以外部设置的roi为准
clue_analyzer.set_roi(m_roi); // 改任务以外部设置的roi为准
if (!clue_analyzer.analyze()) {
return false;
}
@@ -35,4 +35,4 @@ bool asst::InfrastClueImageAnalyzer::clue_analyze()
{
// todo
return false;
}
}

View File

@@ -1,7 +1,8 @@
#pragma once
#pragma once
#include "AbstractImageAnalyzer.h"
namespace asst {
namespace asst
{
class InfrastClueImageAnalyzer : public AbstractImageAnalyzer
{
public:
@@ -10,14 +11,16 @@ namespace asst {
virtual bool analyze() override;
const std::vector<std::pair<Rect, std::string>>& get_result() const noexcept {
const std::vector<std::pair<Rect, std::string>>& get_result() const noexcept
{
return m_result;
}
protected:
bool clue_detect();
bool clue_analyze();
bool m_need_detailed = false; // 是否需要详细分析线索号false时只检测不识别
bool m_need_detailed = false; // 是否需要详细分析线索号false时只检测不识别
std::vector<std::pair<Rect, std::string>> m_result;
};
}

View File

@@ -1,8 +1,9 @@
#include "InfrastClueVacancyImageAnalyzer.h"
#include "InfrastClueVacancyImageAnalyzer.h"
#include "AsstUtils.hpp"
#include "Logger.hpp"
#include "MatchImageAnalyzer.h"
#include "Resource.h"
#include "AsstUtils.hpp"
bool asst::InfrastClueVacancyImageAnalyzer::analyze()
{
@@ -14,9 +15,11 @@ bool asst::InfrastClueVacancyImageAnalyzer::analyze()
resource.task().task_ptr(clue_vacancy + suffix));
analyzer.set_task_info(*task_ptr);
if (!analyzer.analyze()) {
log.trace("no", clue_vacancy, suffix);
continue;
}
Rect rect = analyzer.get_result().rect;
log.trace("has", clue_vacancy, suffix);
#ifdef LOG_TRACE
cv::rectangle(m_image_draw, utils::make_rect<cv::Rect>(rect), cv::Scalar(0, 0, 255), 2);
cv::putText(m_image_draw, suffix, cv::Point(rect.x, rect.y + 1), 0, 1, cv::Scalar(0, 0, 255), 2);
@@ -25,4 +28,4 @@ bool asst::InfrastClueVacancyImageAnalyzer::analyze()
}
return !m_clue_vacancy.empty();
}
}

View File

@@ -1,7 +1,8 @@
#pragma once
#pragma once
#include "AbstractImageAnalyzer.h"
namespace asst {
namespace asst
{
class InfrastClueVacancyImageAnalyzer final : public AbstractImageAnalyzer
{
public:
@@ -12,19 +13,24 @@ namespace asst {
virtual bool analyze() override;
constexpr static int MaxNumOfClue = 7;
void set_to_be_analyzed(std::vector<std::string> to_be_analyzed) noexcept {
void set_to_be_analyzed(std::vector<std::string> to_be_analyzed) noexcept
{
m_to_be_analyzed = std::move(to_be_analyzed);
}
const std::unordered_map<std::string, Rect>& get_vacancy() const noexcept {
const std::unordered_map<std::string, Rect>& get_vacancy() const noexcept
{
return m_clue_vacancy;
}
private:
// 该分析器不支持外部设置ROI
virtual void set_roi(const Rect& roi) noexcept override {
virtual void set_roi(const Rect& roi) noexcept override
{
AbstractImageAnalyzer::set_roi(roi);
}
virtual void set_image(const cv::Mat& image, const Rect& roi) {
virtual void set_image(const cv::Mat& image, const Rect& roi)
{
AbstractImageAnalyzer::set_image(image, roi);
}

View File

@@ -1,4 +1,4 @@
#include "InfrastConfiger.h"
#include "InfrastConfiger.h"
#include <json.h>
@@ -53,8 +53,7 @@ bool asst::InfrastConfiger::parse(const json::value& json)
else {
for (const std::string& pd : products) {
if (std::string pd_reg_key = pd + reg_suffix;
efficient.exist(pd_reg_key))
{
efficient.exist(pd_reg_key)) {
skill.efficient_regex.emplace(pd, efficient.at(pd_reg_key).as_string());
skill.efficient.emplace(pd, 0);
}
@@ -116,8 +115,7 @@ bool asst::InfrastConfiger::parse(const json::value& json)
else {
for (const std::string& pd : products) {
if (std::string pd_reg_key = pd + reg_suffix;
efficient.exist(pd_reg_key))
{
efficient.exist(pd_reg_key)) {
comb.efficient_regex.emplace(pd, efficient.at(pd_reg_key).as_string());
comb.efficient.emplace(pd, 0);
}
@@ -166,8 +164,7 @@ bool asst::InfrastConfiger::parse(const json::value& json)
else {
for (const std::string& pd : products) {
if (std::string pd_reg_key = pd + reg_suffix;
efficient.exist(pd_reg_key))
{
efficient.exist(pd_reg_key)) {
comb.efficient_regex.emplace(pd, efficient.at(pd_reg_key).as_string());
comb.efficient.emplace(pd, 0);
}
@@ -199,4 +196,4 @@ bool asst::InfrastConfiger::parse(const json::value& json)
m_facilities_info.emplace(facility_name, std::move(fac_info));
}
return true;
}
}

View File

@@ -1,4 +1,4 @@
#pragma once
#pragma once
#include "AbstractConfiger.h"
#include <unordered_map>
@@ -6,13 +6,15 @@
#include "AsstDef.h"
namespace asst {
namespace asst
{
class InfrastConfiger : public AbstractConfiger
{
public:
virtual ~InfrastConfiger() = default;
const std::unordered_set<std::string>& get_templ_required() const noexcept {
const std::unordered_set<std::string>& get_templ_required() const noexcept
{
return m_templ_required;
}
const std::unordered_map<std::string, InfrastSkill>&
@@ -20,13 +22,16 @@ namespace asst {
{
return m_skills.at(facility_name);
}
const std::vector<InfrastSkillsGroup>& get_skills_group(const std::string& facility) const {
const std::vector<InfrastSkillsGroup>& get_skills_group(const std::string& facility) const
{
return m_skills_groups.at(facility);
}
const InfrastFacilityInfo& get_facility_info(const std::string& facility) const {
const InfrastFacilityInfo& get_facility_info(const std::string& facility) const
{
return m_facilities_info.at(facility);
}
const std::unordered_map<std::string, InfrastFacilityInfo>& get_facility_info() const noexcept {
const std::unordered_map<std::string, InfrastFacilityInfo>& get_facility_info() const noexcept
{
return m_facilities_info;
}

View File

@@ -1,22 +1,26 @@
#include "InfrastDormTask.h"
#include "InfrastDormTask.h"
#include "Controller.h"
#include "InfrastMoodImageAnalyzer.h"
#include "Logger.hpp"
#include "MatchImageAnalyzer.h"
#include "OcrImageAnalyzer.h"
#include "InfrastMoodImageAnalyzer.h"
#include "Resource.h"
#include "Controller.h"
const std::string asst::InfrastDormTask::FacilityName = "Dorm";
bool asst::InfrastDormTask::run()
{
json::value task_start_json = json::object{
{ "task_type", "InfrastDormTask" },
{ "task_chain", m_task_chain}
{ "task_type", "InfrastDormTask" },
{ "task_chain", m_task_chain }
};
m_callback(AsstMsg::TaskStart, task_start_json, m_callback_arg);
for (; m_cur_dorm_index < m_max_num_of_dorm; ++m_cur_dorm_index) {
if (need_exit()) {
return false;
}
enter_facility(FacilityName, m_cur_dorm_index);
if (!enter_oper_list_page()) {
return false;
@@ -26,9 +30,13 @@ bool asst::InfrastDormTask::run()
int quantity_selected = 0;
while (quantity_selected < MaxNumOfOpers) {
if (need_exit()) {
return false;
}
const auto& image = ctrler.get_image();
InfrastMoodImageAnalyzer mood_analyzer(image);
if (!mood_analyzer.analyze()) {
log.error("mood analyze faild!");
return false;
}
mood_analyzer.sort_result();
@@ -36,14 +44,18 @@ bool asst::InfrastDormTask::run()
int quantity_resting = 0;
for (const auto& mood_info : mood_result) {
if (need_exit()) {
return false;
}
if (quantity_selected >= MaxNumOfOpers) {
log.trace("quantity_selected:", quantity_selected, ", just break");
break;
}
switch (mood_info.smiley.type)
{
switch (mood_info.smiley.type) {
case InfrastSmileyType::Rest:
// 如果当前页面休息完成的人数超过5个说明已经已经把所有心情不满的滑过一遍、没有更多的了直接退出即可
if (++quantity_resting > MaxNumOfOpers) {
log.trace("quantity_resting:", quantity_resting, ", confirm");
click_confirm_button();
return true;
}
@@ -51,12 +63,10 @@ bool asst::InfrastDormTask::run()
case InfrastSmileyType::Work:
case InfrastSmileyType::Distract:
// 干员没有被选择的情况下心情小于30%,或不在工作,就进驻宿舍
if (mood_info.selected == false
&& (mood_info.percentage < m_mood_threshold
|| mood_info.working == false))
{
if (mood_info.selected == false && (mood_info.percentage < m_mood_threshold || mood_info.working == false)) {
ctrler.click(mood_info.rect);
if (++quantity_selected >= MaxNumOfOpers) {
log.trace("quantity_selected:", quantity_selected, ", just break");
break;
}
}
@@ -66,6 +76,7 @@ bool asst::InfrastDormTask::run()
}
}
if (quantity_selected >= MaxNumOfOpers) {
log.trace("quantity_selected:", quantity_selected, ", just break");
break;
}
sync_swipe_of_operlist();
@@ -78,6 +89,8 @@ bool asst::InfrastDormTask::run()
bool asst::InfrastDormTask::click_confirm_button()
{
LogTraceFunction;
const auto task_ptr = std::dynamic_pointer_cast<OcrTaskInfo>(
resource.task().task_ptr("InfrastConfirmButton"));
ctrler.click(task_ptr->specific_rect);
@@ -98,6 +111,9 @@ bool asst::InfrastDormTask::click_confirm_button()
OcrImageAnalyzer analyzer;
analyzer.set_task_info(*task_ptr);
for (int i = 0; i != m_retry_times; ++i) {
if (need_exit()) {
return false;
}
const auto& image = ctrler.get_image();
analyzer.set_image(image);
if (!analyzer.analyze()) {
@@ -107,4 +123,4 @@ bool asst::InfrastDormTask::click_confirm_button()
sleep(sec_cfm_task_ptr->rear_delay);
}
return false;
}
}

View File

@@ -1,7 +1,8 @@
#pragma once
#pragma once
#include "InfrastAbstractTask.h"
namespace asst {
namespace asst
{
class InfrastDormTask : public InfrastAbstractTask
{
public:
@@ -9,12 +10,14 @@ namespace asst {
virtual ~InfrastDormTask() = default;
virtual bool run() override;
void set_mood_threshold(double mood_threshold) noexcept {
void set_mood_threshold(double mood_threshold) noexcept
{
m_mood_threshold = mood_threshold;
}
const static std::string FacilityName;
const static int MaxNumOfOpers = 5;
private:
virtual bool click_confirm_button() override;

View File

@@ -1,8 +1,9 @@
#include "InfrastFacilityImageAnalyzer.h"
#include "InfrastFacilityImageAnalyzer.h"
#include "Resource.h"
#include "MultiMatchImageAnalyzer.h"
#include "AsstUtils.hpp"
#include "Logger.hpp"
#include "MultiMatchImageAnalyzer.h"
#include "Resource.h"
bool asst::InfrastFacilityImageAnalyzer::analyze()
{
@@ -32,13 +33,13 @@ bool asst::InfrastFacilityImageAnalyzer::analyze()
int cor_suffix_index = -1;
for (const auto& [key, task_name] : facility_task_name) {
if (!m_to_be_analyzed.empty()) { // 若为空,则分析所有设施
if (std::find(m_to_be_analyzed.cbegin(), m_to_be_analyzed.cend(), key)
== m_to_be_analyzed.cend()) {
continue;
}
for (const auto& key : m_to_be_analyzed) {
auto find_iter = facility_task_name.find(key);
if (find_iter == facility_task_name.cend()) {
log.error("facility name error", key);
continue;
}
std::string task_name = find_iter->second;
std::vector<MatchRect> cur_facility_result;
// 已知基建缩放状态的时候,只识别这个缩放状态下的就行了
// 否则识别所有状态,直到找出正确的当前缩放状态
@@ -58,9 +59,9 @@ bool asst::InfrastFacilityImageAnalyzer::analyze()
const auto& cur_res = mm_analyzer.get_result();
auto cur_max_iter = std::max_element(cur_res.cbegin(), cur_res.cend(),
[](const MatchRect& lhs, const MatchRect& rhs) -> bool {
return lhs.score < rhs.score;
});
[](const MatchRect& lhs, const MatchRect& rhs) -> bool {
return lhs.score < rhs.score;
});
if (cur_max_iter == cur_res.cend()) {
continue;
}
@@ -91,4 +92,4 @@ bool asst::InfrastFacilityImageAnalyzer::analyze()
}
return !m_result.empty();
}
}

View File

@@ -1,7 +1,8 @@
#pragma once
#pragma once
#include "AbstractImageAnalyzer.h"
namespace asst {
namespace asst
{
class InfrastFacilityImageAnalyzer final : public AbstractImageAnalyzer
{
public:
@@ -11,12 +12,13 @@ namespace asst {
virtual bool analyze() override;
// 若为空,则分析所有设施
void set_to_be_analyzed(std::vector<std::string> facilities) noexcept {
void set_to_be_analyzed(std::vector<std::string> facilities) noexcept
{
m_to_be_analyzed = std::move(facilities);
}
int get_quantity(const std::string& name) const {
int get_quantity(const std::string& name) const
{
if (auto iter = m_result.find(name);
iter == m_result.cend()) {
return 0;
@@ -25,7 +27,8 @@ namespace asst {
return iter->second.size();
}
}
Rect get_rect(const std::string& name, int index) const {
Rect get_rect(const std::string& name, int index) const
{
if (auto iter = m_result.find(name);
iter == m_result.cend()) {
return Rect();
@@ -39,21 +42,25 @@ namespace asst {
}
}
}
const std::unordered_map<std::string, std::vector<MatchRect>>& get_result() const noexcept {
const std::unordered_map<std::string, std::vector<MatchRect>>& get_result() const noexcept
{
return m_result;
}
private:
// 该分析器不支持外部设置ROI
virtual void set_roi(const Rect& roi) noexcept override {
virtual void set_roi(const Rect& roi) noexcept override
{
AbstractImageAnalyzer::set_roi(roi);
}
virtual void set_image(const cv::Mat& image, const Rect& roi) {
virtual void set_image(const cv::Mat& image, const Rect& roi)
{
AbstractImageAnalyzer::set_image(image, roi);
}
// key设施名value所有这种设施的当前Rect例如所有制造站的位置
std::unordered_map<std::string, std::vector<MatchRect>> m_result;
// 需要识别的设施名,若为空,则分析所有设施
// 需要识别的设施名
std::vector<std::string> m_to_be_analyzed;
};
}

View File

@@ -1,26 +1,23 @@
#include "InfrastInfoTask.h"
#include "InfrastInfoTask.h"
#include "Controller.h"
#include "RuntimeStatus.h"
#include "Resource.h"
#include "InfrastFacilityImageAnalyzer.h"
#include "Logger.hpp"
#include "Resource.h"
#include "RuntimeStatus.h"
bool asst::InfrastInfoTask::run()
{
json::value task_start_json = json::object{
{ "task_type", "InfrastInfoTask" },
{ "task_chain", m_task_chain}
{ "task_type", "InfrastInfoTask" },
{ "task_chain", m_task_chain }
};
m_callback(AsstMsg::TaskStart, task_start_json, m_callback_arg);
const auto& image = ctrler.get_image();
InfrastFacilityImageAnalyzer analyzer(image);
std::vector<std::string> all_facilities;
for (auto&& [key, _value] : resource.infrast().get_facility_info()) {
all_facilities.emplace_back(key);
}
analyzer.set_to_be_analyzed({ "Mfg", "Trade", "Power" });
if (!analyzer.analyze()) {
return false;
}
@@ -32,4 +29,4 @@ bool asst::InfrastInfoTask::run()
}
return true;
}
}

View File

@@ -1,7 +1,8 @@
#pragma once
#pragma once
#include "InfrastAbstractTask.h"
namespace asst {
namespace asst
{
class InfrastInfoTask : public InfrastAbstractTask
{
public:
@@ -10,4 +11,4 @@ namespace asst {
virtual bool run() override;
};
}
}

View File

@@ -1,16 +1,16 @@
#include "InfrastMfgTask.h"
#include "InfrastMfgTask.h"
#include "Resource.h"
#include "Controller.h"
#include "MatchImageAnalyzer.h"
#include "Resource.h"
const std::string asst::InfrastMfgTask::FacilityName = "Mfg";
bool asst::InfrastMfgTask::run()
{
json::value task_start_json = json::object{
{ "task_type", "InfrastMfgTask" },
{ "task_chain", m_task_chain}
{ "task_type", "InfrastMfgTask" },
{ "task_chain", m_task_chain }
};
m_callback(AsstMsg::TaskStart, task_start_json, m_callback_arg);
@@ -24,4 +24,4 @@ bool asst::InfrastMfgTask::run()
shift_facility_list();
return true;
}
}

View File

@@ -1,7 +1,8 @@
#pragma once
#pragma once
#include "InfrastProductionTask.h"
namespace asst {
namespace asst
{
class InfrastMfgTask : public InfrastProductionTask
{
public:

View File

@@ -1,37 +1,46 @@
#include "InfrastMoodImageAnalyzer.h"
#include "InfrastMoodImageAnalyzer.h"
#include "Resource.h"
#include "InfrastSmileyImageAnalyzer.h"
#include "MatchImageAnalyzer.h"
#include "AsstUtils.hpp"
#include "InfrastSmileyImageAnalyzer.h"
#include "Logger.hpp"
#include "MatchImageAnalyzer.h"
#include "Resource.h"
bool asst::InfrastMoodImageAnalyzer::analyze()
{
LogTraceFunction;
mood_detect();
mood_analyze();
hash_calc();
selected_analyze();
working_analyze();
for (auto&& info : m_result) {
log.trace(info.hash, info.rect.to_string(),
"smiley:", (int)info.smiley.type, "mood:", info.percentage,
"selected:", info.selected, "working:", info.working);
}
return true;
}
void asst::InfrastMoodImageAnalyzer::sort_result()
{
std::sort(m_result.begin(), m_result.end(),
[](const InfrastOperMoodInfo& lhs, const InfrastOperMoodInfo& rhs) ->bool {
// 先按心情排序,心情低的放前面
if (std::fabs(lhs.percentage - rhs.percentage) > DoubleDiff) {
return lhs.percentage < rhs.percentage;
}
// 心情一样的就按位置排序,左边的放前面
if (std::abs(lhs.rect.x - rhs.rect.x) > 5) {
return lhs.rect.x < rhs.rect.x;
}
else {
return lhs.rect.y < rhs.rect.y;
}
});
[](const InfrastOperMoodInfo& lhs, const InfrastOperMoodInfo& rhs) -> bool {
// 先按心情排序,心情低的放前面
if (std::fabs(lhs.percentage - rhs.percentage) > DoubleDiff) {
return lhs.percentage < rhs.percentage;
}
// 心情一样的就按位置排序,左边的放前面
if (std::abs(lhs.rect.x - rhs.rect.x) > 5) {
return lhs.rect.x < rhs.rect.x;
}
else {
return lhs.rect.y < rhs.rect.y;
}
});
}
bool asst::InfrastMoodImageAnalyzer::mood_detect()
@@ -63,8 +72,7 @@ bool asst::InfrastMoodImageAnalyzer::mood_detect()
prg_rect.y += smiley.rect.y;
// mood_analyze是可以识别长度不够的心情条的
// 这里主要是为了算立绘hash宽度不够的hash不好算直接忽略了
if (prg_rect.x + prg_rect.width >= roi.x + roi.width
|| prg_rect.x < roi.x) {
if (prg_rect.x + prg_rect.width >= roi.x + roi.width || prg_rect.x < roi.x) {
continue;
}
#ifdef LOG_TRACE
@@ -118,15 +126,14 @@ bool asst::InfrastMoodImageAnalyzer::mood_analyze()
cv::Mat prg_gray;
cv::cvtColor(prg_image, prg_gray, cv::COLOR_BGR2GRAY);
int max_white_length = 0; // 最长横扫的白色长度,即作为进度条长度
int max_white_length = 0; // 最长横扫的白色长度,即作为进度条长度
for (int i = 0; i != prg_gray.rows; ++i) {
int cur_white_length = 0;
cv::uint8_t left_value = prg_lower_limit;
for (int j = 0; j != prg_gray.cols; ++j) {
auto value = prg_gray.at<cv::uint8_t>(i, j);
// 当前点的颜色,需要大于最低阈值;且与相邻点的差值不能过大,否则就认为当前点不是进度条
if (value >= prg_lower_limit
&& left_value < value + prg_diff_thres) {
if (value >= prg_lower_limit && left_value < value + prg_diff_thres) {
left_value = value;
++cur_white_length;
if (max_white_length < cur_white_length) {
@@ -145,8 +152,7 @@ bool asst::InfrastMoodImageAnalyzer::mood_analyze()
}
// 如果进度条的长度等于ROI的宽度则说明这个进度条不完整可能图像再往右滑还有一部分进度条
// 所以忽略掉这个结果
if (roi.width != iter->rect.width
&& max_white_length == prg_gray.cols) {
if (roi.width != iter->rect.width && max_white_length == prg_gray.cols) {
iter = m_result.erase(iter);
continue;
}
@@ -186,20 +192,31 @@ bool asst::InfrastMoodImageAnalyzer::selected_analyze()
resource.task().task_ptr("InfrastOperSelected"));
Rect rect_move = selected_task_ptr->rect_move;
MatchImageAnalyzer selected_analyzer(m_image);
selected_analyzer.set_task_info(*selected_task_ptr);
for (auto&& info : m_result) {
Rect selected_rect = rect_move;
cv::Rect selected_rect = utils::make_rect<cv::Rect>(rect_move);
selected_rect.x += info.smiley.rect.x;
selected_rect.y += info.smiley.rect.y;
selected_analyzer.set_roi(selected_rect);
if (selected_analyzer.analyze()) {
info.selected = true;
#ifdef LOG_TRACE
cv::putText(m_image_draw, "SELECTED", cv::Point(selected_rect.x, selected_rect.y + 50), 1, 1, cv::Scalar(0, 0, 255), 2);
#endif
cv::Mat roi = m_image(selected_rect);
cv::Mat hsv, bin;
cv::cvtColor(roi, hsv, cv::COLOR_BGR2HSV);
std::vector<cv::Mat> channels;
cv::split(hsv, channels);
int mask_lowb = selected_task_ptr->mask_range.first;
int mask_uppb = selected_task_ptr->mask_range.second;
int count = 0;
auto& h_channel = channels.at(0);
for (int i = 0; i != h_channel.rows; ++i) {
for (int j = 0; j != h_channel.cols; ++j) {
cv::uint8_t value = h_channel.at<cv::uint8_t>(i, j);
if (mask_lowb < value && value < mask_uppb) {
++count;
}
}
}
log.trace("selected_analyze |", count);
info.selected = count >= selected_task_ptr->templ_threshold;
}
return true;
}
@@ -226,4 +243,4 @@ bool asst::InfrastMoodImageAnalyzer::working_analyze()
}
}
return true;
}
}

View File

@@ -1,7 +1,8 @@
#pragma once
#pragma once
#include "AbstractImageAnalyzer.h"
namespace asst {
namespace asst
{
class InfrastMoodImageAnalyzer final : public AbstractImageAnalyzer
{
public:
@@ -13,16 +14,19 @@ namespace asst {
void sort_result();
const std::vector<InfrastOperMoodInfo>& get_result() const noexcept {
const std::vector<InfrastOperMoodInfo>& get_result() const noexcept
{
return m_result;
}
private:
// 该分析器不支持外部设置ROI
virtual void set_roi(const Rect& roi) noexcept override {
virtual void set_roi(const Rect& roi) noexcept override
{
AbstractImageAnalyzer::set_roi(roi);
}
virtual void set_image(const cv::Mat& image, const Rect& roi) {
virtual void set_image(const cv::Mat& image, const Rect& roi)
{
AbstractImageAnalyzer::set_image(image, roi);
}

View File

@@ -1,12 +1,14 @@
#include "InfrastOfficeTask.h"
#include "InfrastOfficeTask.h"
#include "Controller.h"
const std::string asst::InfrastOfficeTask::FacilityName = "Office";
bool asst::InfrastOfficeTask::run()
{
json::value task_start_json = json::object{
{ "task_type", "InfrastOfficeTask" },
{ "task_chain", m_task_chain}
{ "task_type", "InfrastOfficeTask" },
{ "task_chain", m_task_chain }
};
m_callback(AsstMsg::TaskStart, task_start_json, m_callback_arg);
@@ -18,28 +20,52 @@ bool asst::InfrastOfficeTask::run()
swipe_to_the_right_of_main_ui();
enter_facility(FacilityName, 0);
click_bottomleft_tab();
swipe_to_the_left_of_operlist();
opers_detect_with_swipe();
swipe_to_the_left_of_operlist();
constexpr int retry_times = 1;
for (int i = 0; i <= retry_times; ++i) {
if (need_exit()) {
return false;
}
swipe_to_the_left_of_operlist();
opers_detect_with_swipe();
swipe_to_the_left_of_operlist();
auto find_iter = std::find_if(m_all_available_opers.begin(), m_all_available_opers.end(),
[&](const InfrastOperSkillInfo& info) -> bool {
return info.selected;
});
// 如果之前有干员在,那就不换人,直接退出办公室
if (find_iter != m_all_available_opers.end()) {
m_all_available_opers.erase(find_iter);
auto find_iter = std::find_if(m_all_available_opers.begin(), m_all_available_opers.end(),
[&](const InfrastOperSkillInfo& info) -> bool {
return info.selected;
});
bool need_shift = true;
if (find_iter != m_all_available_opers.end()) {
switch (m_work_mode) {
case InfrastWorkMode::Gentle:
// 如果之前有干员在,那就不换人,直接退出
m_all_available_opers.erase(find_iter);
need_shift = false;
break;
case InfrastWorkMode::Aggressive:
need_shift = true;
ctrler.click(find_iter->rect);
break;
case InfrastWorkMode::Extreme: // TODO
break;
default:
break;
}
}
if (need_shift) {
optimal_calc();
bool ret = opers_choose();
if (!ret) {
m_all_available_opers.clear();
continue;
}
}
break;
}
else {
optimal_calc();
opers_choose();
}
click_confirm_button();
click_return_button();
return true;
}
}

View File

@@ -1,7 +1,8 @@
#pragma once
#pragma once
#include "InfrastProductionTask.h"
namespace asst {
namespace asst
{
class InfrastOfficeTask : public InfrastProductionTask
{
public:
@@ -11,6 +12,7 @@ namespace asst {
const static std::string FacilityName;
const static int MaxNumOfOpers = 1;
private:
};
}
}

View File

@@ -1,12 +1,14 @@
#include "InfrastPowerTask.h"
#include "InfrastPowerTask.h"
#include "Controller.h"
const std::string asst::InfrastPowerTask::FacilityName = "Power";
bool asst::InfrastPowerTask::run()
{
json::value task_start_json = json::object{
{ "task_type", "InfrastPowerTask" },
{ "task_chain", m_task_chain}
{ "task_type", "InfrastPowerTask" },
{ "task_chain", m_task_chain }
};
m_callback(AsstMsg::TaskStart, task_start_json, m_callback_arg);
@@ -16,40 +18,63 @@ bool asst::InfrastPowerTask::run()
// 发电站只能造这一个
set_product("Drone");
swipe_to_the_left_of_main_ui();
for (int i = 0; i != 3; ++i) {
for (int i = 0; i != MaxNumOfFacility; ++i) {
if (need_exit()) {
return false;
}
swipe_to_the_left_of_main_ui();
enter_facility(FacilityName, i);
if (!enter_oper_list_page()) {
return false;
}
swipe_to_the_left_of_operlist();
if (m_all_available_opers.empty()) {
opers_detect_with_swipe();
constexpr int retry_times = 1;
for (int i = 0; i <= retry_times; ++i) {
swipe_to_the_left_of_operlist();
}
else {
opers_detect();
}
auto find_iter = std::find_if(m_all_available_opers.begin(), m_all_available_opers.end(),
[&](const InfrastOperSkillInfo& info) -> bool {
return info.selected;
});
// 如果之前有干员在,那就不换人,直接退出当前发电站
if (find_iter != m_all_available_opers.end()) {
m_all_available_opers.erase(find_iter);
}
else {
optimal_calc();
opers_choose();
}
if (m_all_available_opers.empty()) {
opers_detect_with_swipe();
swipe_to_the_left_of_operlist();
}
else {
opers_detect();
}
auto find_iter = std::find_if(m_all_available_opers.begin(), m_all_available_opers.end(),
[&](const InfrastOperSkillInfo& info) -> bool {
return info.selected;
});
bool need_shift = true;
if (find_iter != m_all_available_opers.end()) {
switch (m_work_mode) {
case InfrastWorkMode::Gentle:
// 如果之前有干员在,那就不换人,直接退出
m_all_available_opers.erase(find_iter);
need_shift = false;
break;
case InfrastWorkMode::Aggressive:
need_shift = true;
ctrler.click(find_iter->rect);
break;
case InfrastWorkMode::Extreme: // TODO
break;
default:
break;
}
}
if (need_shift) {
optimal_calc();
bool ret = opers_choose();
if (!ret) {
m_all_available_opers.clear();
continue;
}
}
break;
}
click_confirm_button();
click_return_button();
}
return true;
}
}

View File

@@ -1,7 +1,8 @@
#pragma once
#pragma once
#include "InfrastProductionTask.h"
namespace asst {
namespace asst
{
class InfrastPowerTask : public InfrastProductionTask
{
public:
@@ -11,6 +12,8 @@ namespace asst {
const static std::string FacilityName;
const static int MaxNumOfOpers = 1;
const static int MaxNumOfFacility = 3;
private:
};
}

View File

@@ -1,16 +1,16 @@
#include "InfrastProductionTask.h"
#include "InfrastProductionTask.h"
#include <algorithm>
#include <calculator/calculator.hpp>
#include "Resource.h"
#include "AsstUtils.hpp"
#include "Controller.h"
#include "InfrastSkillsImageAnalyzer.h"
#include "MultiMatchImageAnalyzer.h"
#include "MatchImageAnalyzer.h"
#include "AsstUtils.hpp"
#include "Logger.hpp"
#include "MatchImageAnalyzer.h"
#include "MultiMatchImageAnalyzer.h"
#include "Resource.h"
#include "RuntimeStatus.h"
//bool asst::InfrastProductionTask::run()
@@ -35,16 +35,22 @@
bool asst::InfrastProductionTask::shift_facility_list()
{
LogTraceFunction;
facility_list_detect();
if (need_exit()) {
return false;
}
const auto tab_task_ptr = std::dynamic_pointer_cast<MatchTaskInfo>(
resource.task().task_ptr("InfrastFacilityListTab" + m_facility));
MatchImageAnalyzer add_analyzer;
const auto add_task_ptr = std::dynamic_pointer_cast<MatchTaskInfo>(
resource.task().task_ptr("InfrastAddOperator" + m_facility));
resource.task().task_ptr("InfrastAddOperator" + m_facility + m_work_mode_name));
add_analyzer.set_task_info(*add_task_ptr);
for (const Rect& tab : m_facility_list_tabs) {
if (need_exit()) {
return false;
}
ctrler.click(tab);
sleep(tab_task_ptr->rear_delay);
@@ -52,9 +58,18 @@ bool asst::InfrastProductionTask::shift_facility_list()
const auto& image = ctrler.get_image();
add_analyzer.set_image(image);
if (!add_analyzer.analyze()) {
log.info("no add button, just continue");
continue;
}
Rect add_button = add_analyzer.get_result().rect;
auto& rect = add_analyzer.get_result().rect;
Rect add_button = rect;
auto& rect_move = add_task_ptr->rect_move;
if (!rect_move.empty()) {
add_button.x += rect_move.x;
add_button.y += rect_move.y;
add_button.width = rect_move.width;
add_button.height = rect_move.height;
}
/* 识别当前正在造什么 */
MatchImageAnalyzer product_analyzer(image);
@@ -71,21 +86,34 @@ bool asst::InfrastProductionTask::shift_facility_list()
}
}
set_product(cur_product);
log.info("cur product", cur_product);
/* 进入干员选择页面 */
ctrler.click(add_button);
sleep(add_task_ptr->rear_delay);
click_clear_button();
swipe_to_the_left_of_operlist();
if (m_all_available_opers.empty()) {
opers_detect_with_swipe();
constexpr int retry_times = 1;
for (int i = 0; i <= retry_times; ++i) {
if (need_exit()) {
return false;
}
click_clear_button();
swipe_to_the_left_of_operlist();
if (m_all_available_opers.empty()) {
opers_detect_with_swipe();
swipe_to_the_left_of_operlist();
}
else {
opers_detect();
}
optimal_calc();
bool ret = opers_choose();
if (!ret) {
m_all_available_opers.clear();
continue;
}
break;
}
else {
opers_detect();
}
optimal_calc();
opers_choose();
click_confirm_button();
}
return true;
@@ -93,11 +121,16 @@ bool asst::InfrastProductionTask::shift_facility_list()
bool asst::InfrastProductionTask::opers_detect_with_swipe()
{
LogTraceFunction;
m_all_available_opers.clear();
int first_number = 0;
while (true) {
if (need_exit()) {
return false;
}
size_t num = opers_detect();
log.trace("opers_detect return", num);
// 这里本来是判断不相等就可以退出循环。
// 但是有时候滑动会把一个干员挡住一半一个页面完整的干员真的只有10个所以加个2的差值
@@ -117,6 +150,7 @@ bool asst::InfrastProductionTask::opers_detect_with_swipe()
size_t asst::InfrastProductionTask::opers_detect()
{
LogTraceFunction;
const auto& image = ctrler.get_image();
InfrastSkillsImageAnalyzer skills_analyzer(image);
@@ -129,7 +163,8 @@ size_t asst::InfrastProductionTask::opers_detect()
max_num_of_opers_per_page = (std::max)(max_num_of_opers_per_page, cur_all_info.size());
for (const auto& cur_info : cur_all_info) {
auto find_iter = std::find_if(m_all_available_opers.cbegin(), m_all_available_opers.cend(),
auto find_iter = std::find_if(
m_all_available_opers.cbegin(), m_all_available_opers.cend(),
[&cur_info](const InfrastOperSkillInfo& info) -> bool {
int dist = utils::hamming(cur_info.hash, info.hash);
return dist < HashDistThres;
@@ -139,27 +174,7 @@ size_t asst::InfrastProductionTask::opers_detect()
continue;
}
auto pred_info = cur_info;
// 根据正则,计算当前干员的实际效率
for (auto&& [product, formula] : pred_info.skills_comb.efficient_regex) {
std::string cur_formula = formula;
for (size_t pos = 0; pos != std::string::npos;) {
pos = cur_formula.find('[', pos);
if (pos == std::string::npos) {
break;
}
size_t rp_pos = cur_formula.find(']', pos);
if (rp_pos == std::string::npos) {
break;
// TODO 报错!
}
std::string status_key = cur_formula.substr(pos + 1, rp_pos - pos - 1);
int status_value = std::any_cast<int>(status.get(status_key));
cur_formula.replace(pos, rp_pos - pos + 1, std::to_string(status_value));
}
int eff = calculator::eval(cur_formula);
pred_info.skills_comb.efficient[product] = eff;
}
pred_info.skills_comb = efficient_regex_calc(pred_info.skills_comb);
m_all_available_opers.emplace_back(std::move(pred_info));
}
return cur_all_info.size();
@@ -167,6 +182,7 @@ size_t asst::InfrastProductionTask::opers_detect()
bool asst::InfrastProductionTask::optimal_calc()
{
LogTraceFunction;
auto& facility_info = resource.infrast().get_facility_info(m_facility);
int max_num_of_opers = facility_info.max_num_of_opers;
@@ -179,9 +195,9 @@ bool asst::InfrastProductionTask::optimal_calc()
optimal_opers.reserve(max_num_of_opers);
double max_efficient = 0;
std::sort(m_all_available_opers.begin(), m_all_available_opers.end(),
[&](const InfrastOperSkillInfo& lhs, const InfrastOperSkillInfo& rhs) -> bool {
return lhs.skills_comb.efficient.at(m_product) > rhs.skills_comb.efficient.at(m_product);
});
[&](const InfrastOperSkillInfo& lhs, const InfrastOperSkillInfo& rhs) -> bool {
return lhs.skills_comb.efficient.at(m_product) > rhs.skills_comb.efficient.at(m_product);
});
for (const auto& oper : m_all_available_opers) {
std::string skill_str;
@@ -215,6 +231,7 @@ bool asst::InfrastProductionTask::optimal_calc()
cur_opers.reserve(max_num_of_opers);
double cur_efficient = 0;
// 条件判断,不符合的直接过滤掉
bool meet_condition = true;
for (const auto& [cond, cond_value] : group.conditions) {
if (!status.exist(cond)) {
continue;
@@ -222,12 +239,17 @@ bool asst::InfrastProductionTask::optimal_calc()
// TODO这里做成除了不等于还可计算大于、小于等不同条件的
int cur_value = std::any_cast<int>(status.get(cond));
if (cur_value != cond_value) {
continue;
meet_condition = false;
break;
}
}
if (!meet_condition) {
continue;
}
// necessary里的技能一个都不能少
for (const InfrastSkillsComb& nec_skills : group.necessary) {
auto find_iter = std::find_if(cur_available_opers.cbegin(), cur_available_opers.cend(),
auto find_iter = std::find_if(
cur_available_opers.cbegin(), cur_available_opers.cend(),
[&](const InfrastOperSkillInfo& arg) -> bool {
return arg.skills_comb == nec_skills;
});
@@ -236,7 +258,13 @@ bool asst::InfrastProductionTask::optimal_calc()
break;
}
cur_opers.emplace_back(nec_skills);
cur_efficient += nec_skills.efficient.at(m_product);
if (auto iter = nec_skills.efficient_regex.find(m_product);
iter != nec_skills.efficient_regex.cend()) {
cur_efficient += efficient_regex_calc(nec_skills).efficient.at(m_product);
}
else {
cur_efficient += nec_skills.efficient.at(m_product);
}
cur_available_opers.erase(find_iter);
}
if (group_unavailable) {
@@ -245,21 +273,27 @@ bool asst::InfrastProductionTask::optimal_calc()
// 排个序,因为产物不同,效率可能会发生变化,所以配置文件里默认的顺序不一定准确
auto optional = group.optional;
std::sort(optional.begin(), optional.end(),
[&](const InfrastSkillsComb& lhs, const InfrastSkillsComb& rhs) -> bool {
return lhs.efficient.at(m_product) > rhs.efficient.at(m_product);
});
[&](const InfrastSkillsComb& lhs, const InfrastSkillsComb& rhs) -> bool {
return lhs.efficient.at(m_product) > rhs.efficient.at(m_product);
});
// 可能有多个干员有同样的技能,所以这里需要循环找同一个技能,直到找不到为止
for (const InfrastSkillsComb& opt : optional) {
auto find_iter = cur_available_opers.cbegin();
while (cur_opers.size() != max_num_of_opers) {
find_iter = std::find_if(find_iter, cur_available_opers.cend(),
[&](const InfrastOperSkillInfo& arg) -> bool {
return arg.skills_comb.skills == opt.skills;
});
[&](const InfrastOperSkillInfo& arg) -> bool {
return arg.skills_comb.skills == opt.skills;
});
if (find_iter != cur_available_opers.cend()) {
cur_opers.emplace_back(opt);
cur_efficient += opt.efficient.at(m_product);
if (auto iter = opt.efficient_regex.find(m_product);
iter != opt.efficient_regex.cend()) {
cur_efficient += efficient_regex_calc(opt).efficient.at(m_product);
}
else {
cur_efficient += opt.efficient.at(m_product);
}
find_iter = cur_available_opers.erase(find_iter);
}
else {
@@ -313,7 +347,12 @@ bool asst::InfrastProductionTask::optimal_calc()
bool asst::InfrastProductionTask::opers_choose()
{
LogTraceFunction;
bool has_error = false;
while (true) {
if (need_exit()) {
return false;
}
const auto& image = ctrler.get_image();
InfrastSkillsImageAnalyzer skills_analyzer(image);
@@ -326,12 +365,26 @@ bool asst::InfrastProductionTask::opers_choose()
auto cur_all_info = skills_analyzer.get_result();
// 这个情况一般是滑动/识别出错了,把所有的干员都滑过去了
if (cur_all_info.empty()) {
if (!has_error) {
has_error = true;
// 倒回去再来一遍
swipe_to_the_left_of_operlist();
continue;
}
else {
// 如果已经出过一次错了那就可能不是opers_choose出错而是之前的opers_detect出错了
return false;
}
}
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 InfrastOperSkillInfo& lhs) -> bool {
return lhs.skills_comb == opt_iter->skills_comb;
});
[&](const InfrastOperSkillInfo& lhs) -> bool {
return lhs.skills_comb == opt_iter->skills_comb;
});
if (find_iter == cur_all_info.cend()) {
++opt_iter;
continue;
@@ -344,9 +397,9 @@ bool asst::InfrastProductionTask::opers_choose()
selected_hash.emplace_back(find_iter->hash);
{
auto avlb_iter = std::find_if(m_all_available_opers.cbegin(), m_all_available_opers.cend(),
[&](const InfrastOperSkillInfo& lhs) -> bool {
return lhs.skills_comb == opt_iter->skills_comb;
});
[&](const InfrastOperSkillInfo& lhs) -> bool {
return lhs.skills_comb == opt_iter->skills_comb;
});
m_all_available_opers.erase(avlb_iter);
}
cur_all_info.erase(find_iter);
@@ -363,8 +416,35 @@ bool asst::InfrastProductionTask::opers_choose()
return true;
}
asst::InfrastSkillsComb asst::InfrastProductionTask::efficient_regex_calc(InfrastSkillsComb skills_comb) const
{
// 根据正则,计算当前干员的实际效率
for (auto&& [product, formula] : skills_comb.efficient_regex) {
std::string cur_formula = formula;
for (size_t pos = 0; pos != std::string::npos;) {
pos = cur_formula.find('[', pos);
if (pos == std::string::npos) {
break;
}
size_t rp_pos = cur_formula.find(']', pos);
if (rp_pos == std::string::npos) {
break;
// TODO 报错!
}
std::string status_key = cur_formula.substr(pos + 1, rp_pos - pos - 1);
int status_value = std::any_cast<int>(status.get(status_key));
cur_formula.replace(pos, rp_pos - pos + 1, std::to_string(status_value));
}
int eff = calculator::eval(cur_formula);
skills_comb.efficient[product] = eff;
}
return skills_comb;
}
bool asst::InfrastProductionTask::facility_list_detect()
{
LogTraceFunction;
m_facility_list_tabs.clear();
const auto& image = ctrler.get_image();
@@ -384,4 +464,4 @@ bool asst::InfrastProductionTask::facility_list_detect()
}
return true;
}
}

View File

@@ -1,10 +1,11 @@
#pragma once
#pragma once
#include "InfrastAbstractTask.h"
#include "AsstDef.h"
namespace asst {
namespace asst
{
// 生产类设施的任务,适用于制造站/贸易站
class InfrastProductionTask : public InfrastAbstractTask
{
@@ -12,19 +13,23 @@ namespace asst {
using InfrastAbstractTask::InfrastAbstractTask;
virtual ~InfrastProductionTask() = default;
//virtual bool run() override;
void set_facility(std::string facility_name) noexcept {
void set_facility(std::string facility_name) noexcept
{
m_facility = std::move(facility_name);
}
void set_product(std::string product_name) noexcept {
void set_product(std::string product_name) noexcept
{
m_product = std::move(product_name);
}
protected:
bool shift_facility_list();
bool facility_list_detect();
bool opers_detect_with_swipe();
size_t opers_detect(); // 返回当前页面的干员数
size_t opers_detect(); // 返回当前页面的干员数
bool optimal_calc();
bool opers_choose();
InfrastSkillsComb efficient_regex_calc(InfrastSkillsComb skill_info) const;
constexpr static int HashDistThres = 25;

View File

@@ -1,20 +1,20 @@
#include "InfrastReceptionTask.h"
#include "InfrastReceptionTask.h"
#include "Controller.h"
#include "Resource.h"
#include "InfrastClueVacancyImageAnalyzer.h"
#include "InfrastClueImageAnalyzer.h"
#include "MatchImageAnalyzer.h"
#include "InfrastClueVacancyImageAnalyzer.h"
#include "Logger.hpp"
#include "MatchImageAnalyzer.h"
#include "ProcessTaskImageAnalyzer.h"
#include "Resource.h"
const std::string asst::InfrastReceptionTask::FacilityName = "Reception";
bool asst::InfrastReceptionTask::run()
{
json::value task_start_json = json::object{
{ "task_type", "InfrastReceptionTask" },
{ "task_chain", m_task_chain}
{ "task_type", "InfrastReceptionTask" },
{ "task_chain", m_task_chain }
};
m_callback(AsstMsg::TaskStart, task_start_json, m_callback_arg);
@@ -38,8 +38,12 @@ bool asst::InfrastReceptionTask::run()
bool asst::InfrastReceptionTask::harvest_clue()
{
LogTraceFunction;
std::vector<std::string> tasks_vec = { "InfrastClueNew" };
while (!tasks_vec.empty()) {
if (need_exit()) {
return false;
}
ProcessTaskImageAnalyzer analyzer(ctrler.get_image(), tasks_vec);
if (!analyzer.analyze()) {
break;
@@ -71,37 +75,16 @@ bool asst::InfrastReceptionTask::harvest_clue()
bool asst::InfrastReceptionTask::proc_clue()
{
LogTraceFunction;
const static std::string clue_vacancy = "InfrastClueVacancy";
const static std::vector<std::string> clue_suffix = {
"No1", "No2", "No3", "No4", "No5", "No6", "No7" };
"No1", "No2", "No3", "No4", "No5", "No6", "No7"
};
cv::Mat image;
for (const std::string& clue : clue_suffix) {
// 先识别线索的空位
image = ctrler.get_image();
InfrastClueVacancyImageAnalyzer vacancy_analyzer(image);
vacancy_analyzer.set_to_be_analyzed({ clue });
if (!vacancy_analyzer.analyze()) {
continue;
}
// 点开线索的空位
Rect vacancy = vacancy_analyzer.get_vacancy().cbegin()->second;
ctrler.click(vacancy);
int delay = resource.task().task_ptr(clue_vacancy + clue)->rear_delay;
sleep(delay);
proc_vacancy();
// 识别右边列表中的线索,然后用最底下的那个(一般都是剩余时间最短的)
swipe_to_the_bottom_of_clue_list_on_the_right();
image = ctrler.get_image();
InfrastClueImageAnalyzer clue_analyzer(image);
if (!clue_analyzer.analyze()) {
continue;
}
ctrler.click(clue_analyzer.get_result().back().first);
sleep(delay);
}
// 开启线索交流,“解锁线索”
image = ctrler.get_image();
cv::Mat image = ctrler.get_image();
MatchImageAnalyzer unlock_analyzer(image);
const auto unlock_task_ptr = std::dynamic_pointer_cast<MatchTaskInfo>(
resource.task().task_ptr("UnlockClues"));
@@ -110,6 +93,7 @@ bool asst::InfrastReceptionTask::proc_clue()
ctrler.click(unlock_analyzer.get_result().rect);
sleep(unlock_task_ptr->rear_delay);
click_bottomleft_tab();
proc_vacancy();
image = ctrler.get_image();
}
@@ -136,37 +120,97 @@ bool asst::InfrastReceptionTask::proc_clue()
return true;
}
bool asst::InfrastReceptionTask::proc_vacancy()
{
LogTraceFunction;
const static std::string clue_vacancy = "InfrastClueVacancy";
const static std::vector<std::string> clue_suffix = {
"No1", "No2", "No3", "No4", "No5", "No6", "No7"
};
for (const std::string& clue : clue_suffix) {
if (need_exit()) {
return false;
}
// 先识别线索的空位
cv::Mat image = ctrler.get_image();
InfrastClueVacancyImageAnalyzer vacancy_analyzer(image);
vacancy_analyzer.set_to_be_analyzed({ clue });
if (!vacancy_analyzer.analyze()) {
continue;
}
// 点开线索的空位
Rect vacancy = vacancy_analyzer.get_vacancy().cbegin()->second;
ctrler.click(vacancy);
int delay = resource.task().task_ptr(clue_vacancy + clue)->rear_delay;
sleep(delay);
// 识别右边列表中的线索,然后用最底下的那个(一般都是剩余时间最短的)
swipe_to_the_bottom_of_clue_list_on_the_right();
image = ctrler.get_image();
InfrastClueImageAnalyzer clue_analyzer(image);
if (!clue_analyzer.analyze()) {
continue;
}
ctrler.click(clue_analyzer.get_result().back().first);
sleep(delay);
}
return true;
}
bool asst::InfrastReceptionTask::shift()
{
LogTraceFunction;
const auto& image = ctrler.get_image();
MatchImageAnalyzer add_analyzer(image);
const auto add_task_ptr = std::dynamic_pointer_cast<MatchTaskInfo>(
resource.task().task_ptr("InfrastAddOperator" + m_facility));
add_analyzer.set_task_info(*add_task_ptr);
const auto raw_task_ptr = resource.task().task_ptr("InfrastAddOperator" + m_facility + m_work_mode_name);
switch (raw_task_ptr->algorithm) {
case AlgorithmType::JustReturn:
if (raw_task_ptr->action == ProcessTaskAction::ClickRect) {
ctrler.click(raw_task_ptr->specific_rect);
}
break;
case AlgorithmType::MatchTemplate: {
const auto add_task_ptr = std::dynamic_pointer_cast<MatchTaskInfo>(raw_task_ptr);
add_analyzer.set_task_info(*add_task_ptr);
if (!add_analyzer.analyze()) {
return true;
if (!add_analyzer.analyze()) {
return true;
}
ctrler.click(add_analyzer.get_result().rect);
} break;
default:
break;
}
ctrler.click(add_analyzer.get_result().rect);
sleep(add_task_ptr->rear_delay);
sleep(raw_task_ptr->rear_delay);
swipe_to_the_left_of_operlist();
click_clear_button();
constexpr int retry_times = 1;
for (int i = 0; i <= retry_times; ++i) {
if (need_exit()) {
return false;
}
swipe_to_the_left_of_operlist();
click_clear_button();
opers_detect_with_swipe();
swipe_to_the_left_of_operlist();
optimal_calc();
opers_choose();
opers_detect_with_swipe();
swipe_to_the_left_of_operlist();
optimal_calc();
bool ret = opers_choose();
if (!ret) {
m_all_available_opers.clear();
continue;
}
break;
}
click_confirm_button();
return true;
}
bool asst::InfrastReceptionTask::swipe_to_the_bottom_of_clue_list_on_the_right()
{
LogTraceFunction;
static Rect begin_rect = resource.task().task_ptr("InfrastClueOnTheRightSwipeBegin")->specific_rect;
static Rect end_rect = resource.task().task_ptr("InfrastClueOnTheRightSwipeEnd")->specific_rect;
static int duration = resource.task().task_ptr("InfrastClueOnTheRightSwipeBegin")->pre_delay;
@@ -178,4 +222,4 @@ bool asst::InfrastReceptionTask::swipe_to_the_bottom_of_clue_list_on_the_right()
}
sleep(extra_delay);
return false;
}
}

View File

@@ -1,7 +1,8 @@
#pragma once
#pragma once
#include "InfrastProductionTask.h"
namespace asst {
namespace asst
{
class InfrastReceptionTask : public InfrastProductionTask
{
public:
@@ -10,9 +11,11 @@ namespace asst {
virtual bool run() override;
const static std::string FacilityName;
private:
bool harvest_clue();
bool proc_clue();
bool proc_vacancy();
bool shift();
bool swipe_to_the_bottom_of_clue_list_on_the_right();
};

View File

@@ -1,13 +1,15 @@
#include "InfrastSkillsImageAnalyzer.h"
#include "InfrastSkillsImageAnalyzer.h"
#include "Resource.h"
#include "InfrastSmileyImageAnalyzer.h"
#include "AsstUtils.hpp"
#include "MatchImageAnalyzer.h"
#include "InfrastSmileyImageAnalyzer.h"
#include "Logger.hpp"
#include "MatchImageAnalyzer.h"
#include "Resource.h"
bool asst::InfrastSkillsImageAnalyzer::analyze()
{
LogTraceFunction;
m_skills_detected.clear();
m_skills_splited.clear();
m_result.clear();
@@ -18,26 +20,35 @@ bool asst::InfrastSkillsImageAnalyzer::analyze()
skill_analyze();
for (auto&& info : m_result) {
std::string skill_str;
for (auto&& skill : info.skills_comb.skills) {
skill_str += skill.names.front() + " ";
}
log.trace(info.hash, info.rect.to_string(), skill_str);
}
return true;
}
void asst::InfrastSkillsImageAnalyzer::sort_result()
{
LogTraceFunction;
// 按位置排个序
std::sort(m_result.begin(), m_result.end(),
[](const auto& lhs, const auto& rhs) -> bool {
if (std::abs(lhs.rect.x - rhs.rect.x) < 5) { // x差距较小则理解为是同一排的按y排序
return lhs.rect.y < rhs.rect.y;
}
else {
return lhs.rect.x < rhs.rect.x;
}
}
);
[](const auto& lhs, const auto& rhs) -> bool {
if (std::abs(lhs.rect.x - rhs.rect.x) < 5) { // x差距较小则理解为是同一排的按y排序
return lhs.rect.y < rhs.rect.y;
}
else {
return lhs.rect.x < rhs.rect.x;
}
});
}
bool asst::InfrastSkillsImageAnalyzer::skills_detect()
{
LogTraceFunction;
const auto upper_task_ptr = resource.task().task_ptr("InfrastSkillsUpper");
const auto lower_task_ptr = resource.task().task_ptr("InfrastSkillsLower");
const auto hash_task_ptr = resource.task().task_ptr("InfrastSkillsHash");
@@ -73,8 +84,7 @@ bool asst::InfrastSkillsImageAnalyzer::skills_detect()
skills_rect.height = skills_height;
// 超过ROI边界了
if (skills_rect.x + skills_rect.width > roi.x + roi.width
|| skills_rect.x < roi.x) {
if (skills_rect.x + skills_rect.width > roi.x + roi.width || skills_rect.x < roi.x) {
continue;
}
#ifdef LOG_TRACE
@@ -98,6 +108,7 @@ bool asst::InfrastSkillsImageAnalyzer::skills_detect()
bool asst::InfrastSkillsImageAnalyzer::skills_split()
{
LogTraceFunction;
const auto task_ptr = std::dynamic_pointer_cast<MatchTaskInfo>(
resource.task().task_ptr("InfrastSkills"));
const auto thres = task_ptr->hist_threshold;
@@ -113,7 +124,7 @@ bool asst::InfrastSkillsImageAnalyzer::skills_split()
}
cv::Mat image_roi = m_image(utils::make_rect<cv::Rect>(roi));
std::vector<Rect> skills_vec; // 单个干员的所有技能
std::vector<Rect> skills_vec; // 单个干员的所有技能
for (int i = 0; i != MaxNumOfSkills; ++i) {
int x = i * skill_width + spacing * i;
Rect skill_rect(x, 0, skill_width, roi.height);
@@ -144,6 +155,7 @@ bool asst::InfrastSkillsImageAnalyzer::skills_split()
bool asst::InfrastSkillsImageAnalyzer::skill_analyze()
{
LogTraceFunction;
const auto task_ptr = std::dynamic_pointer_cast<MatchTaskInfo>(
resource.task().task_ptr("InfrastSkills"));
@@ -151,13 +163,8 @@ bool asst::InfrastSkillsImageAnalyzer::skill_analyze()
skill_analyzer.set_mask_range(task_ptr->mask_range);
skill_analyzer.set_threshold(task_ptr->templ_threshold);
const auto selected_task_ptr = std::dynamic_pointer_cast<MatchTaskInfo>(
resource.task().task_ptr("InfrastOperSelected"));
MatchImageAnalyzer selected_analyzer(m_image);
selected_analyzer.set_task_info(*selected_task_ptr);
for (const auto& [hash, skills_rect_vec] : m_skills_splited) {
std::unordered_set<InfrastSkill> skills_set; // 单个干员的全部技能
std::unordered_set<InfrastSkill> skills_set; // 单个干员的全部技能
std::string log_str = "[ ";
for (const Rect& skill_rect : skills_rect_vec) {
skill_analyzer.set_roi(skill_rect);
@@ -189,9 +196,9 @@ bool asst::InfrastSkillsImageAnalyzer::skill_analyze()
// 匹配得分最高的id作为基准排除有识别错误其他的技能混进来了的情况
// 即排除容器中,除了有同一个技能的不同等级,还有别的技能的情况
auto max_iter = std::max_element(possible_skills.begin(), possible_skills.end(),
[](const auto& lhs, const auto& rhs) -> bool {
return lhs.second.score < rhs.second.score;
});
[](const auto& lhs, const auto& rhs) -> bool {
return lhs.second.score < rhs.second.score;
});
std::string base_id = max_iter->first.id;
size_t level_pos = 0;
// 倒着找第一个不是数字的。前面就是技能基础id名字后面的数字就是技能等级
@@ -207,8 +214,7 @@ bool asst::InfrastSkillsImageAnalyzer::skill_analyze()
if (size_t find_pos = skill.id.find(base_id);
find_pos != std::string::npos) {
std::string cur_skill_level = skill.id.substr(base_id.size());
if (max_level.empty()
|| cur_skill_level > max_level) {
if (max_level.empty() || cur_skill_level > max_level) {
max_level = cur_skill_level;
most_confident_skills = skill;
}
@@ -233,16 +239,7 @@ bool asst::InfrastSkillsImageAnalyzer::skill_analyze()
// 识别该干员是否被选中。数据结构设计的不太合理,先这么凑合用了
int smiley_x = skills_rect_vec.front().x - task_ptr->rect_move.x;
int smiley_y = skills_rect_vec.front().y - task_ptr->rect_move.y;
Rect selected_rect = selected_task_ptr->rect_move;
selected_rect.x += smiley_x;
selected_rect.y += smiley_y;
selected_analyzer.set_roi(selected_rect);
if (selected_analyzer.analyze()) {
info.selected = true;
}
else {
info.selected = false;
}
info.selected = selected_analyze(smiley_x, smiley_y);
m_result.emplace_back(std::move(info));
}
@@ -250,4 +247,39 @@ bool asst::InfrastSkillsImageAnalyzer::skill_analyze()
return true;
}
return false;
}
}
bool asst::InfrastSkillsImageAnalyzer::selected_analyze(int smiley_x, int smiley_y)
{
LogTraceFunction;
const auto selected_task_ptr = std::dynamic_pointer_cast<MatchTaskInfo>(
resource.task().task_ptr("InfrastOperSelected"));
cv::Rect selected_rect = utils::make_rect<cv::Rect>(selected_task_ptr->rect_move);
selected_rect.x += smiley_x;
selected_rect.y += smiley_y;
#ifdef LOG_TRACE
cv::Mat draw = m_image_draw.clone();
cv::rectangle(draw, selected_rect, cv::Scalar(0, 0, 255), 2);
#endif // LOG_TRACE
cv::Mat roi = m_image(selected_rect);
cv::Mat hsv, bin;
cv::cvtColor(roi, hsv, cv::COLOR_BGR2HSV);
std::vector<cv::Mat> channels;
cv::split(hsv, channels);
int mask_lowb = selected_task_ptr->mask_range.first;
int mask_uppb = selected_task_ptr->mask_range.second;
int count = 0;
auto& h_channel = channels.at(0);
for (int i = 0; i != h_channel.rows; ++i) {
for (int j = 0; j != h_channel.cols; ++j) {
cv::uint8_t value = h_channel.at<cv::uint8_t>(i, j);
if (mask_lowb < value && value < mask_uppb) {
++count;
}
}
}
log.trace("selected_analyze |", count);
return count >= selected_task_ptr->templ_threshold;
}

View File

@@ -1,11 +1,12 @@
#pragma once
#pragma once
#include "AbstractImageAnalyzer.h"
#include <unordered_map>
#include "AsstDef.h"
namespace asst {
namespace asst
{
class InfrastSkillsImageAnalyzer final : public AbstractImageAnalyzer
{
public:
@@ -17,26 +18,31 @@ namespace asst {
void sort_result();
const std::vector<InfrastOperSkillInfo>& get_result() const noexcept {
const std::vector<InfrastOperSkillInfo>& get_result() const noexcept
{
return m_result;
}
void set_facility(std::string facility_name) noexcept {
void set_facility(std::string facility_name) noexcept
{
m_facility = std::move(facility_name);
}
constexpr static int MaxNumOfSkills = 2; // 单个干员最多有几个基建技能
constexpr static int MaxNumOfSkills = 2; // 单个干员最多有几个基建技能
private:
// 该分析器不支持外部设置ROI
virtual void set_roi(const Rect& roi) noexcept override {
virtual void set_roi(const Rect& roi) noexcept override
{
AbstractImageAnalyzer::set_roi(roi);
}
virtual void set_image(const cv::Mat& image, const Rect& roi) {
virtual void set_image(const cv::Mat& image, const Rect& roi)
{
AbstractImageAnalyzer::set_image(image, roi);
}
bool skills_detect(); // 检测出所有技能区域
bool skills_split(); // 拆分成一个一个的区域
bool skill_analyze(); // 识别每个技能区域是啥
bool skills_detect(); // 检测出所有技能区域
bool skills_split(); // 拆分成一个一个的区域
bool skill_analyze(); // 识别每个技能区域是啥
bool selected_analyze(int smiley_x, int smiley_y); // 识别干员是否被选中
std::string m_facility;
// skills_detect()的结果key是hashvalue是单个干员的全部技能区域
@@ -46,4 +52,4 @@ namespace asst {
// skill_analyze()的结果,最终结果
std::vector<InfrastOperSkillInfo> m_result;
};
}
}

View File

@@ -1,16 +1,15 @@
#include "InfrastSmileyImageAnalyzer.h"
#include "InfrastSmileyImageAnalyzer.h"
#include "Resource.h"
#include "MultiMatchImageAnalyzer.h"
#include "AsstUtils.hpp"
#include "MultiMatchImageAnalyzer.h"
#include "Resource.h"
bool asst::InfrastSmileyImageAnalyzer::analyze()
{
const static std::unordered_map<InfrastSmileyType, std::string> smiley_map =
{
{InfrastSmileyType::Rest, "InfrastSmileyOnRest"},
{InfrastSmileyType::Work, "InfrastSmileyOnWork"},
{InfrastSmileyType::Distract, "InfrastSmileyOnDistract"}
const static std::unordered_map<InfrastSmileyType, std::string> smiley_map = {
{ InfrastSmileyType::Rest, "InfrastSmileyOnRest" },
{ InfrastSmileyType::Work, "InfrastSmileyOnWork" },
{ InfrastSmileyType::Distract, "InfrastSmileyOnDistract" }
};
m_result.clear();
@@ -35,4 +34,4 @@ bool asst::InfrastSmileyImageAnalyzer::analyze()
}
m_result = std::move(temp_result);
return true;
}
}

View File

@@ -1,9 +1,10 @@
#pragma once
#pragma once
#include "AbstractImageAnalyzer.h"
#include "AsstDef.h"
namespace asst {
namespace asst
{
class InfrastSmileyImageAnalyzer : public AbstractImageAnalyzer
{
public:
@@ -12,9 +13,11 @@ namespace asst {
virtual bool analyze() override;
const std::vector<InfrastSmileyInfo>& get_result() const noexcept {
const std::vector<InfrastSmileyInfo>& get_result() const noexcept
{
return m_result;
}
protected:
std::vector<InfrastSmileyInfo> m_result;
};

View File

@@ -1,12 +1,12 @@
#include "InfrastTradeTask.h"
#include "InfrastTradeTask.h"
const std::string asst::InfrastTradeTask::FacilityName = "Trade";
bool asst::InfrastTradeTask::run()
{
json::value task_start_json = json::object{
{ "task_type", "InfrastTradeTask" },
{ "task_chain", m_task_chain}
{ "task_type", "InfrastTradeTask" },
{ "task_chain", m_task_chain }
};
m_callback(AsstMsg::TaskStart, task_start_json, m_callback_arg);
@@ -20,4 +20,4 @@ bool asst::InfrastTradeTask::run()
shift_facility_list();
return true;
}
}

View File

@@ -1,7 +1,8 @@
#pragma once
#pragma once
#include "InfrastProductionTask.h"
namespace asst {
namespace asst
{
class InfrastTradeTask : public InfrastProductionTask
{
public:
@@ -13,4 +14,4 @@ namespace asst {
private:
};
}
}

View File

@@ -1,4 +1,4 @@
#include "ItemConfiger.h"
#include "ItemConfiger.h"
#include <json.h>
@@ -9,4 +9,4 @@ bool asst::ItemConfiger::parse(const json::value& json)
m_item_name.emplace(id, std::move(name));
}
return true;
}
}

View File

@@ -1,10 +1,11 @@
#pragma once
#pragma once
#include "AbstractConfiger.h"
#include <unordered_map>
namespace asst {
namespace asst
{
class ItemConfiger : public AbstractConfiger
{
public:
@@ -21,10 +22,12 @@ namespace asst {
return empty;
}
}
const std::unordered_map<std::string, int>& get_drop_count() const noexcept {
const std::unordered_map<std::string, int>& get_drop_count() const noexcept
{
return m_drop_count;
}
std::unordered_map<std::string, int>& get_drop_count() noexcept {
std::unordered_map<std::string, int>& get_drop_count() noexcept
{
return m_drop_count;
}
int get_drop_count(const std::string& id) const noexcept
@@ -41,7 +44,8 @@ namespace asst {
{
m_drop_count.emplace(std::move(id), count);
}
void increase_drop_count(std::string id, int count) {
void increase_drop_count(std::string id, int count)
{
m_drop_count[std::move(id)] += count;
}
void clear_drop_count() noexcept

View File

@@ -1,17 +1,19 @@
#pragma once
#pragma once
#include <fstream>
#include <mutex>
#include <iostream>
#include <vector>
#include <type_traits>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <mutex>
#include <type_traits>
#include <vector>
#include "AsstUtils.hpp"
#include "Version.h"
namespace asst {
class Logger {
namespace asst
{
class Logger
{
public:
~Logger() = default;
@@ -25,19 +27,19 @@ namespace asst {
}
template <typename... Args>
inline void trace(Args &&... args)
inline void trace(Args&&... args)
{
constexpr static std::string_view level = "TRC";
log(level, std::forward<Args>(args)...);
}
template <typename... Args>
inline void info(Args &&... args)
inline void info(Args&&... args)
{
constexpr static std::string_view level = "INF";
log(level, std::forward<Args>(args)...);
}
template <typename... Args>
inline void error(Args &&... args)
inline void error(Args&&... args)
{
constexpr static std::string_view level = "ERR";
log(level, std::forward<Args>(args)...);
@@ -45,6 +47,7 @@ namespace asst {
const std::string m_log_filename = asst::utils::get_cur_dir() + "asst.log";
const std::string m_log_bak_filename = asst::utils::get_cur_dir() + "asst.bak.log";
private:
Logger()
{
@@ -63,8 +66,7 @@ namespace asst {
}
}
}
catch (...)
{
catch (...) {
;
}
}
@@ -80,14 +82,14 @@ namespace asst {
}
template <typename... Args>
void log(const std::string_view& level, Args &&... args)
void log(const std::string_view& level, Args&&... args)
{
std::unique_lock<std::mutex> trace_lock(m_trace_mutex);
char buff[128] = { 0 };
sprintf_s(buff, "[%s][%s][Px%x][Tx%x]",
asst::utils::get_format_time().c_str(),
level.data(), _getpid(), ::GetCurrentThreadId());
asst::utils::get_format_time().c_str(),
level.data(), _getpid(), ::GetCurrentThreadId());
stream_args<true>(std::cout, buff, std::forward<Args>(args)...);
std::ofstream ofs(m_log_filename, std::ios::out | std::ios::app);
@@ -96,7 +98,7 @@ namespace asst {
}
template <bool ToGbk = false, typename T, typename... Args>
inline void stream_args(std::ostream& os, T&& first, Args && ...rest)
inline void stream_args(std::ostream& os, T&& first, Args&&... rest)
{
stream<ToGbk, T>()(os, std::forward<T>(first));
stream_args<ToGbk>(os, std::forward<Args>(rest)...);
@@ -127,7 +129,8 @@ namespace asst {
std::mutex m_trace_mutex;
};
class LoggerAux {
class LoggerAux
{
public:
LoggerAux(const std::string& func_name)
: m_func_name(func_name),
@@ -139,8 +142,9 @@ namespace asst {
{
auto duration = std::chrono::system_clock::now() - m_start_time;
Logger::get_instance().trace(m_func_name, " | leave,",
std::chrono::duration_cast<std::chrono::milliseconds>(duration).count(), "ms");
std::chrono::duration_cast<std::chrono::milliseconds>(duration).count(), "ms");
}
private:
std::string m_func_name;
std::chrono::time_point<std::chrono::system_clock> m_start_time;
@@ -148,6 +152,6 @@ namespace asst {
static auto& log = Logger::get_instance();
#define LogTraceFunction LoggerAux _func_aux(__FUNCTION__)
#define LogTraceScope LoggerAux _func_aux
}
#define LogTraceFunction LoggerAux _func_aux(__FUNCTION__)
#define LogTraceScope LoggerAux _func_aux
}

View File

@@ -1,8 +1,8 @@
#include "MatchImageAnalyzer.h"
#include "MatchImageAnalyzer.h"
#include "Resource.h"
#include "AsstUtils.hpp"
#include "Logger.hpp"
#include "Resource.h"
asst::MatchImageAnalyzer::MatchImageAnalyzer(const cv::Mat& image, const Rect& roi, std::string templ_name, double templ_thres, double hist_thres)
: AbstractImageAnalyzer(image, roi),
@@ -56,6 +56,12 @@ bool asst::MatchImageAnalyzer::match_templ(const cv::Mat& templ)
cv::Mat matched;
cv::Mat image_roi = m_image(utils::make_rect<cv::Rect>(m_roi));
if (templ.rows > image_roi.cols || templ.cols > image_roi.cols) {
log.error("templ size is too large",
"image_roi size:", image_roi.cols, image_roi.cols,
"templ size:", templ.cols, templ.rows);
return false;
}
if (m_mask_range.first == m_mask_range.second) {
cv::matchTemplate(image_roi, templ, matched, cv::TM_CCOEFF_NORMED);
}
@@ -69,7 +75,7 @@ bool asst::MatchImageAnalyzer::match_templ(const cv::Mat& templ)
cv::Point min_loc, max_loc;
cv::minMaxLoc(matched, &min_val, &max_val, &min_loc, &max_loc);
if (max_val > m_templ_thres * 0.7) { // 得分太低的肯定不对,没必要打印
if (max_val > m_templ_thres * 0.7) { // 得分太低的肯定不对,没必要打印
log.trace("match_templ |", m_templ_name, "score:", max_val, "point:", max_loc);
}
@@ -88,7 +94,7 @@ bool asst::MatchImageAnalyzer::comp_hist(const cv::Mat& hist, const cv::Rect roi
cv::Mat image_roi = m_image(utils::make_rect<cv::Rect>(m_roi))(roi);
double score = 1.0 - cv::compareHist(to_hist(image_roi), hist, cv::HISTCMP_BHATTACHARYYA);
if (score > 0.7) { // 得分太低的肯定不对,没必要打印
if (score > 0.7) { // 得分太低的肯定不对,没必要打印
log.trace("comp_hist |", m_templ_name, "score:", score);
}
@@ -100,4 +106,4 @@ bool asst::MatchImageAnalyzer::comp_hist(const cv::Mat& hist, const cv::Rect roi
else {
return false;
}
}
}

View File

@@ -1,7 +1,8 @@
#pragma once
#pragma once
#include "AbstractImageAnalyzer.h"
namespace asst {
namespace asst
{
class MatchImageAnalyzer : public AbstractImageAnalyzer
{
public:
@@ -10,23 +11,29 @@ namespace asst {
virtual ~MatchImageAnalyzer() = default;
virtual bool analyze() override;
void set_use_cache(bool is_use) noexcept {
void set_use_cache(bool is_use) noexcept
{
m_use_cache = is_use;
}
void set_mask_range(int lower, int upper) {
void set_mask_range(int lower, int upper)
{
m_mask_range = std::make_pair(lower, upper);
}
void set_mask_range(std::pair<int, int> mask_range) {
void set_mask_range(std::pair<int, int> mask_range)
{
m_mask_range = std::move(mask_range);
}
void set_templ_name(std::string templ_name) noexcept {
void set_templ_name(std::string templ_name) noexcept
{
m_templ_name = std::move(templ_name);
}
void set_threshold(double templ_thres, double hist_thres = 0.0) noexcept {
void set_threshold(double templ_thres, double hist_thres = 0.0) noexcept
{
m_templ_thres = templ_thres;
m_hist_thres = hist_thres;
}
void set_task_info(MatchTaskInfo task_info) noexcept {
void set_task_info(MatchTaskInfo task_info) noexcept
{
m_use_cache = task_info.cache;
m_mask_range = std::move(task_info.mask_range);
m_templ_name = std::move(task_info.templ_name);
@@ -38,7 +45,8 @@ namespace asst {
//const std::vector<MatchRect>& get_result() const noexcept {
// return m_result;
//}
const MatchRect& get_result() const noexcept {
const MatchRect& get_result() const noexcept
{
return m_result;
}

View File

@@ -1,4 +1,4 @@
#include "MultiMatchImageAnalyzer.h"
#include "MultiMatchImageAnalyzer.h"
#include "Logger.hpp"
#include "Resource.h"
@@ -29,15 +29,14 @@ void asst::MultiMatchImageAnalyzer::sort_result()
{
// 按位置排个序
std::sort(m_result.begin(), m_result.end(),
[](const MatchRect& lhs, const MatchRect& rhs) -> bool {
if (std::abs(lhs.rect.y - rhs.rect.y) < 5) { // y差距较小则理解为是同一排的按x排序
return lhs.rect.x < rhs.rect.x;
}
else {
return lhs.rect.y < rhs.rect.y;
}
}
);
[](const MatchRect& lhs, const MatchRect& rhs) -> bool {
if (std::abs(lhs.rect.y - rhs.rect.y) < 5) { // y差距较小则理解为是同一排的按x排序
return lhs.rect.x < rhs.rect.x;
}
else {
return lhs.rect.y < rhs.rect.y;
}
});
}
bool asst::MultiMatchImageAnalyzer::multi_match_templ(const cv::Mat& templ)
@@ -65,13 +64,11 @@ bool asst::MultiMatchImageAnalyzer::multi_match_templ(const cv::Mat& templ)
// 如果有两个点离得太近,只取里面得分高的那个
// 一般相邻的都是刚刚push进去的这里倒序快一点
for (auto iter = m_result.rbegin(); iter != m_result.rend(); ++iter) {
if (std::abs(j + m_roi.x - iter->rect.x) < mini_distance
&& std::abs(i + m_roi.y - iter->rect.y) < mini_distance)
{
if (std::abs(j + m_roi.x - iter->rect.x) < mini_distance && std::abs(i + m_roi.y - iter->rect.y) < mini_distance) {
if (iter->score < value) {
iter->rect = rect;
iter->score = value;
} // else 这个点就放弃了
} // else 这个点就放弃了
need_push = false;
break;
}
@@ -94,4 +91,4 @@ bool asst::MultiMatchImageAnalyzer::multi_match_templ(const cv::Mat& templ)
}
return false;
}
}

View File

@@ -1,7 +1,8 @@
#pragma once
#pragma once
#include "AbstractImageAnalyzer.h"
namespace asst {
namespace asst
{
class MultiMatchImageAnalyzer : public AbstractImageAnalyzer
{
public:
@@ -12,25 +13,31 @@ namespace asst {
virtual bool analyze() override;
void sort_result(); // 按位置排序,左上角的排在前面
void set_mask_range(int lower, int upper) {
void set_mask_range(int lower, int upper)
{
m_mask_range = std::make_pair(lower, upper);
}
void set_mask_range(std::pair<int, int> mask_range) {
void set_mask_range(std::pair<int, int> mask_range)
{
m_mask_range = std::move(mask_range);
}
void set_templ_name(std::string templ_name) noexcept {
void set_templ_name(std::string templ_name) noexcept
{
m_templ_name = std::move(templ_name);
}
void set_threshold(double templ_thres) noexcept {
void set_threshold(double templ_thres) noexcept
{
m_templ_thres = templ_thres;
}
void set_task_info(MatchTaskInfo task_info) noexcept {
void set_task_info(MatchTaskInfo task_info) noexcept
{
m_mask_range = std::move(task_info.mask_range);
m_templ_name = std::move(task_info.templ_name);
m_templ_thres = task_info.templ_threshold;
set_roi(task_info.roi);
}
const std::vector<MatchRect>& get_result() const noexcept {
const std::vector<MatchRect>& get_result() const noexcept
{
return m_result;
}

View File

@@ -1,8 +1,8 @@
#include "OcrImageAnalyzer.h"
#include "OcrImageAnalyzer.h"
#include "Resource.h"
#include "AsstUtils.hpp"
#include "Logger.hpp"
#include "Resource.h"
bool asst::OcrImageAnalyzer::analyze()
{
@@ -56,4 +56,4 @@ bool asst::OcrImageAnalyzer::analyze()
else {
return false;
}
}
}

View File

@@ -1,13 +1,14 @@
#pragma once
#pragma once
#include "AbstractImageAnalyzer.h"
#include <vector>
#include <unordered_map>
#include <functional>
#include <unordered_map>
#include <vector>
#include "AsstDef.h"
namespace asst {
namespace asst
{
class OcrImageAnalyzer : public AbstractImageAnalyzer
{
public:
@@ -16,24 +17,29 @@ namespace asst {
virtual bool analyze() override;
void set_required(std::vector<std::string> required, bool full_match = false) noexcept {
void set_required(std::vector<std::string> required, bool full_match = false) noexcept
{
m_required = std::move(required);
m_full_match = full_match;
}
void set_replace(std::unordered_map<std::string, std::string> replace) noexcept {
void set_replace(std::unordered_map<std::string, std::string> replace) noexcept
{
m_replace = std::move(replace);
}
void set_task_info(OcrTaskInfo task_info) noexcept {
void set_task_info(OcrTaskInfo task_info) noexcept
{
m_required = std::move(task_info.text);
m_full_match = task_info.need_full_match;
m_replace = std::move(task_info.replace_map);
set_roi(task_info.roi);
}
void set_pred(const TextRectProc& pred) {
void set_pred(const TextRectProc& pred)
{
m_pred = pred;
}
const std::vector<TextRect>& get_result() const noexcept {
const std::vector<TextRect>& get_result() const noexcept
{
return m_ocr_result;
}

View File

@@ -1,15 +1,14 @@
#include "OcrPack.h"
#include "OcrPack.h"
#include <opencv2/opencv.hpp>
#include <OcrLiteOnnx/OcrLiteCaller.h>
#include <opencv2/opencv.hpp>
#include "AsstUtils.hpp"
#include "Logger.hpp"
asst::OcrPack::OcrPack()
: m_ocr_ptr(std::make_unique<OcrLiteCaller>())
{
}
{}
asst::OcrPack::~OcrPack() = default;
@@ -45,9 +44,9 @@ std::vector<asst::TextRect> asst::OcrPack::recognize(const cv::Mat & image, cons
constexpr bool mostAngle = false;
OcrResult ocr_results = m_ocr_ptr->detect(image,
padding, maxSideLen,
boxScoreThresh, boxThresh,
unClipRatio, doAngle, mostAngle);
padding, maxSideLen,
boxScoreThresh, boxThresh,
unClipRatio, doAngle, mostAngle);
std::vector<TextRect> result;
std::string log_str_raw;
@@ -84,4 +83,4 @@ std::vector<asst::TextRect> asst::OcrPack::recognize(const cv::Mat & image, cons
return pred(tr);
};
return recognize(image(utils::make_rect<cv::Rect>(roi)), rect_cor);
}
}

View File

@@ -1,17 +1,19 @@
#pragma once
#pragma once
#include "AbstractResource.h"
#include <memory>
#include <functional>
#include <memory>
#include "AsstDef.h"
class OcrLiteCaller;
namespace cv {
namespace cv
{
class Mat;
}
namespace asst {
namespace asst
{
class OcrPack final : public AbstractResource
{
public:
@@ -25,7 +27,6 @@ namespace asst {
std::vector<TextRect> recognize(const cv::Mat& image, const Rect& roi, const TextRectProc& pred = nullptr);
private:
std::unique_ptr<OcrLiteCaller> m_ocr_ptr;
};
}

View File

@@ -1,10 +1,11 @@
#include "PenguinPack.h"
#include "PenguinPack.h"
#include <filesystem>
#include <opencv2/opencv.hpp>
#include <json.h>
namespace penguin {
#include <opencv2/opencv.hpp>
namespace penguin
{
#include <penguin-stats-recognize/penguin_wasm.h>
}
@@ -46,7 +47,7 @@ bool asst::PenguinPack::load_json(const std::string& stage_path, const std::stri
json::object cvt_stage_json;
try {
for (const json::value& stage_info : stage_json.as_array()) {
if (!stage_info.exist("dropInfos")) { // 这种一般是以前的活动关,现在已经关闭了的
if (!stage_info.exist("dropInfos")) { // 这种一般是以前的活动关,现在已经关闭了的
continue;
}
std::string key = stage_info.at("code").as_string();
@@ -91,4 +92,4 @@ bool asst::PenguinPack::load_templ(const std::string& item_id, const std::string
penguin::load_templ(item_id.c_str(), buf.data(), buf.size());
return true;
}
}

View File

@@ -1,12 +1,14 @@
#pragma once
#pragma once
#include "AbstractResource.h"
namespace cv {
namespace cv
{
class Mat;
}
namespace asst {
namespace asst
{
class PenguinPack final : public AbstractResource
{
public:
@@ -17,6 +19,7 @@ namespace asst {
void set_language(const std::string& server);
std::string recognize(const cv::Mat& image);
private:
bool load_json(const std::string& stage_path, const std::string& hash_path);
bool load_templ(const std::string& item_id, const std::string& path);

View File

@@ -1,13 +1,13 @@
#include "PenguinUploader.h"
#include "PenguinUploader.h"
#include <Windows.h>
#include <json.h>
#include "AsstUtils.hpp"
#include "Logger.hpp"
#include "Resource.h"
#include "Version.h"
#include "Logger.hpp"
#include "AsstUtils.hpp"
bool asst::PenguinUploader::upload(const std::string& rec_res)
{
@@ -89,4 +89,4 @@ bool asst::PenguinUploader::request_penguin(const std::string& body)
::CloseHandle(pipe_read);
::CloseHandle(pipe_child_write);
return true;
}
}

View File

@@ -1,18 +1,21 @@
#pragma once
#pragma once
#include <string>
namespace json {
namespace json
{
class value;
}
namespace asst {
namespace asst
{
class PenguinUploader
{
public:
static bool upload(const std::string& rec_res);
private:
static std::string cvt_json(const std::string& rec_res);
static bool request_penguin(const std::string& body);
};
}
}

View File

@@ -1,4 +1,4 @@
#include "ProcessTask.h"
#include "ProcessTask.h"
#include <chrono>
#include <random>
@@ -16,13 +16,14 @@ using namespace asst;
bool ProcessTask::run()
{
json::value task_start_json = json::object{
{ "task_type", "ProcessTask" },
{ "task_chain", m_task_chain},
{ "tasks", json::array(m_cur_tasks_name)}
{ "task_type", "ProcessTask" },
{ "task_chain", m_task_chain },
{ "tasks", json::array(m_cur_tasks_name) }
};
m_callback(AsstMsg::TaskStart, task_start_json, m_callback_arg);
ProcessTaskImageAnalyzer analyzer(ctrler.get_image(), m_cur_tasks_name);
const auto& image = ctrler.get_image();
ProcessTaskImageAnalyzer analyzer(image, m_cur_tasks_name);
if (!analyzer.analyze()) {
return false;
}
@@ -44,8 +45,8 @@ bool ProcessTask::run()
{ "type", static_cast<int>(task_info_ptr->action) },
{ "exec_times", task_info_ptr->exec_times },
{ "max_times", task_info_ptr->max_times },
{ "task_type", "ProcessTask"},
{ "algorithm", static_cast<int>(task_info_ptr->algorithm)}
{ "task_type", "ProcessTask" },
{ "algorithm", static_cast<int>(task_info_ptr->algorithm) }
};
m_callback(AsstMsg::TaskMatched, callback_json, m_callback_arg);
@@ -73,9 +74,8 @@ bool ProcessTask::run()
case ProcessTaskAction::ClickSelf:
exec_click_task(rect);
break;
case ProcessTaskAction::ClickRand:
{
static const Rect full_rect(0, 0, GeneralConfiger::WindowWidthDefault, GeneralConfiger::WindowHeightDefault);
case ProcessTaskAction::ClickRand: {
static const Rect full_rect(0, 0, image.cols, image.rows);
exec_click_task(full_rect);
} break;
case ProcessTaskAction::SwipeToTheLeft:
@@ -85,11 +85,10 @@ bool ProcessTask::run()
case ProcessTaskAction::DoNothing:
break;
case ProcessTaskAction::Stop:
m_callback(AsstMsg::ProcessTaskStopAction, json::object{ {"task_chain", m_task_chain} }, m_callback_arg);
m_callback(AsstMsg::ProcessTaskStopAction, json::object{ { "task_chain", m_task_chain } }, m_callback_arg);
need_stop = true;
break;
case ProcessTaskAction::StageDrops:
{
case ProcessTaskAction::StageDrops: {
cv::Mat image = ctrler.get_image(true);
std::string res = resource.penguin().recognize(image);
m_callback(AsstMsg::StageDrops, json::parse(res).value(), m_callback_arg);
@@ -102,8 +101,7 @@ bool ProcessTask::run()
if (opt.penguin_report) {
PenguinUploader::upload(res);
}
}
break;
} break;
default:
break;
}
@@ -170,22 +168,26 @@ void asst::ProcessTask::exec_swipe_task(ProcessTaskAction action)
if (!delay_random()) {
return;
}
auto& width = resource.cfg().WindowWidthDefault;
auto& height = resource.cfg().WindowWidthDefault;
const auto&& [width, height] = ctrler.get_scale_size();
const static Rect right_rect(width * 0.8, height * 0.4, width * 0.1, height * 0.2);
const static Rect right_rect(width * 0.8,
height * 0.4,
width * 0.1,
height * 0.2);
const static Rect left_rect(width * 0.1, height * 0.4, width * 0.1, height * 0.2);
const static Rect left_rect(width * 0.1,
height * 0.4,
width * 0.1,
height * 0.2);
switch (action)
{
switch (action) {
case asst::ProcessTaskAction::SwipeToTheLeft:
ctrler.swipe(left_rect, right_rect);
break;
case asst::ProcessTaskAction::SwipeToTheRight:
ctrler.swipe(right_rect, left_rect);
break;
default: // 走不到这里TODO 报个错
default: // 走不到这里TODO 报个错
break;
}
}
}

View File

@@ -1,9 +1,10 @@
#pragma once
#pragma once
#include "AbstractTask.h"
#include "AsstDef.h"
namespace asst {
namespace asst
{
// 流程任务,按照配置文件里的设置的流程运行
class ProcessTask : public AbstractTask
{
@@ -13,7 +14,8 @@ namespace asst {
virtual bool run() override;
virtual void set_tasks(const std::vector<std::string>& cur_tasks_name) {
virtual void set_tasks(const std::vector<std::string>& cur_tasks_name)
{
m_cur_tasks_name = cur_tasks_name;
}
@@ -24,4 +26,4 @@ namespace asst {
std::vector<std::string> m_cur_tasks_name;
};
}
}

View File

@@ -1,10 +1,10 @@
#include "ProcessTaskImageAnalyzer.h"
#include "ProcessTaskImageAnalyzer.h"
#include "Resource.h"
#include "MatchImageAnalyzer.h"
#include "OcrImageAnalyzer.h"
#include "AsstUtils.hpp"
#include "Logger.hpp"
#include "MatchImageAnalyzer.h"
#include "OcrImageAnalyzer.h"
#include "Resource.h"
asst::ProcessTaskImageAnalyzer::ProcessTaskImageAnalyzer(const cv::Mat& image, std::vector<std::string> tasks_name)
: AbstractImageAnalyzer(image),
@@ -22,6 +22,7 @@ bool asst::ProcessTaskImageAnalyzer::match_analyze(std::shared_ptr<TaskInfo> tas
}
const auto match_task_ptr = std::dynamic_pointer_cast<MatchTaskInfo>(task_ptr);
m_match_analyzer->set_task_info(*match_task_ptr);
m_match_analyzer->correct_roi();
if (m_match_analyzer->analyze()) {
m_result = match_task_ptr;
@@ -69,6 +70,7 @@ bool asst::ProcessTaskImageAnalyzer::ocr_analyze(std::shared_ptr<TaskInfo> task_
// 识别区域文字,并加入缓存
auto analyze_roi = [&](const Rect& roi, bool is_appeared = false) -> bool {
m_ocr_analyzer->set_roi(roi);
m_ocr_analyzer->correct_roi();
bool ret = m_ocr_analyzer->analyze();
const auto& ocr_result = m_ocr_analyzer->get_result();
@@ -88,8 +90,8 @@ bool asst::ProcessTaskImageAnalyzer::ocr_analyze(std::shared_ptr<TaskInfo> task_
// 在曾经识别到过的历史区域里识别
for (const Rect& region : ocr_task_ptr->region_of_appeared) {
static auto& max_width = resource.cfg().WindowWidthDefault;
static auto& max_height = resource.cfg().WindowHeightDefault;
static auto& max_width = WindowWidthDefault;
static auto& max_height = WindowHeightDefault;
bool ret = analyze_roi(region.center_zoom(2.0, max_width, max_height), true);
if (ret) {
log.trace("ProcessTaskImageAnalyzer::ocr_analyze | found in appeared");
@@ -116,8 +118,7 @@ bool asst::ProcessTaskImageAnalyzer::analyze()
for (const std::string& task_name : m_tasks_name) {
auto task_ptr = resource.task().task_ptr(task_name);
switch (task_ptr->algorithm)
{
switch (task_ptr->algorithm) {
case AlgorithmType::JustReturn:
m_result = task_ptr;
return true;
@@ -142,4 +143,4 @@ void asst::ProcessTaskImageAnalyzer::set_image(const cv::Mat & image)
{
AbstractImageAnalyzer::set_image(image);
reset();
}
}

View File

@@ -1,13 +1,14 @@
#pragma once
#pragma once
#include "AbstractImageAnalyzer.h"
#include <vector>
#include <string>
#include <memory>
#include <string>
#include <vector>
#include "AsstDef.h"
namespace asst {
namespace asst
{
class OcrImageAnalyzer;
class MatchImageAnalyzer;
@@ -22,21 +23,27 @@ namespace asst {
virtual bool analyze() override;
virtual void set_image(const cv::Mat& image) override;
void set_tasks(std::vector<std::string> tasks_name) {
void set_tasks(std::vector<std::string> tasks_name)
{
m_tasks_name = std::move(tasks_name);
}
std::shared_ptr<TaskInfo> get_result() const noexcept {
std::shared_ptr<TaskInfo> get_result() const noexcept
{
return m_result;
}
const Rect& get_rect() const noexcept {
const Rect& get_rect() const noexcept
{
return m_result_rect;
}
private:
// 该分析器不支持外部设置ROI
virtual void set_roi(const Rect& roi) noexcept override {
virtual void set_roi(const Rect& roi) noexcept override
{
AbstractImageAnalyzer::set_roi(roi);
}
virtual void set_image(const cv::Mat& image, const Rect& roi) {
virtual void set_image(const cv::Mat& image, const Rect& roi)
{
AbstractImageAnalyzer::set_image(image, roi);
}
bool match_analyze(std::shared_ptr<TaskInfo> task_ptr);

View File

@@ -1,4 +1,4 @@
#include "RecruitConfiger.h"
#include "RecruitConfiger.h"
#include <algorithm>
@@ -29,12 +29,9 @@ bool asst::RecruitConfiger::parse(const json::value& json)
}
// 按干员等级排个序
std::sort(m_all_opers.begin(), m_all_opers.end(), [](
const auto& lhs,
const auto& rhs)
-> bool {
return lhs.level > rhs.level;
});
std::sort(m_all_opers.begin(), m_all_opers.end(), [](const auto& lhs, const auto& rhs) -> bool {
return lhs.level > rhs.level;
});
return true;
}
}

Some files were not shown because too many files have changed in this diff Show More