Doc: Delete doc, move to WIKI

This commit is contained in:
LmeSzinc 2020-09-03 22:20:17 +08:00
parent 5b38d82f06
commit df569b2dfe
70 changed files with 1 additions and 2617 deletions

View File

@ -1,146 +0,0 @@
# FAQ 常见问题
Here list some frequently asked questions (FAQ).
这里列举了一些常见问题.
## Why Alas is not running 为什么Alas不运行
```
retire_method = one_click_retire
retire_amount = retire_all
enhance_favourite = no
enable_exception = yes
scout_hp_weights = 1000,1000,1000
INFO | <<< SETTINGS SAVED >>>
```
Please run other funtions.
The function you are running is the "Setting", is used to save settings. You see this because settings save is succeed. You can choose other functions to run, such as "Main_chapter" and "Event".
请运行其他功能.
你运行的功能是"设置", 是用来保存设置的. 你看见这个是因为设置保存成功了, 你可以运行其他功能比如"主线图"和"活动图".
## Why Alas get stuck in reward loop 为什么Alas卡在收获里
```
INFO | Reward loop wait
INFO | [Reward_loop_wait] 20 min
```
Please check your settings of "stop conditions".
You see this because Alas triggered a stop condition, all task you gave to Alas have finished. Alas go into reward loop to prevent player get nothing for a whole day. If you don't want Alas to stop, don't set any stop conditions.
请检查你的"停止条件"设置.
你看见这个是因为Alas触发了停止条件, 你给Alas的所有任务已经完成. Alas进入收获循环来避免玩家一天什么都没有得到. 如果你不希望Alas停下, 不要设置任何停止条件.
## Why Alas get stuck in emotion recover 为什么Alas卡在心情回复里
```
INFO | Click ( 507, 457) @ C3
INFO | Combat preparation.
INFO | [Emotion recovered] 2020-06-26 23:42:00
INFO | [Emotion recovered] 2020-06-26 23:42:00
INFO | [Emotion recovered] 2020-06-26 23:42:00
```
Because Alas is waiting for mood recovered.
If you think this is not what you expected, please check your setting in "Mood control". If you want to continue even if it's red face, turn off "enable_emotion_reduce" and turn on "ignore_low_emotion_warn". If you changed your fleet by hand, update `EmotionRecord` in `config/alas.ini`.
因为Alas正在等待心情回复.
如果你认为这不是你希望的, 请检查你的"心情设置". 如果你希望红脸出击, 关掉"启用心情消耗", 并开启"无视红脸出击警告". 如果你手动调整了队伍, 在 `config/alas.ini` 中更新 `EmotionRecord`.
## How to run Reward + Main module 怎样同时运行主线图出击和收获
You don't need to do anything, This is automatic.
When you running "Main_chapter", "Event", "Raid", Alas will check reward from time to time according to your settings.
你不需要做任何操作, 这是自动的
当你运行"主线图", "活动图", "共斗活动"的时候, Alas会根据设置, 时不时地检查收获.
## How to use only one fleet 怎样只使用一支舰队
```
WARNING | Mob fleet 1 and boss fleet 1 is the same
WARNING | They should to be set to different fleets
```
```
WARNING | You should use 2 fleets from chapter 7 to 13
WARNING | Current: mob fleet 1, boss fleet 0
```
Please use 2 fleets.
Don't try to edit the code to bypass these checks, because logics in every maps are hardcoded in 2 fleets. Use only one fleet will cause error. Using 2 fleets helps reducing the posibility of BOSS fleet get stuck by mob enemies from chapter 7 to chapter 9.
请使用两队.
不要试图修改代码来绕过这些检查, 因为每张地图的逻辑都是按两队写死在代码里的. 只使用一队会导致报错. 在第七章到第九章, 使用两队有助于降低BOSS队被道中怪堵住的概率.
## Starting from current page is not supported 不支持从当前界面启动
```
INFO | <<< UI ENSURE >>>
INFO | Unknown ui page
INFO | Unable to goto page_main
WARNING | Starting from current page is not supported
WARNING | Supported page: ['page_main', 'page_campaign', 'page_fleet', 'page_exercise', 'page_daily', 'page_event', 'page_sp', 'page_mission', 'page_raid']
WARNING | Supported page: Any page with a "HOME" button on the upper-right
```
Please check your page in game when you starting Alas.
Alas can goto the page it need automatically, but only allow starting at these pages: page_main, page_campaign, page_fleet, page_exercise, page_daily, page_event, page_sp, page_mission, page_raid. Alas can also start at any page with the "HOME" button on the upper-right. Most pages in game have that, except page_main itself, dorm, meowfficer.
请检查你在启动Alas时的游戏界面.
Alas可以自动切换到需要的游戏界面, 但是只允许在这些界面下启动: 主界面, 出击, 编队, 演习, 每日, 活动, SP活动, 任务领取, 共斗活动. Alas也可以在右上角有"一键回港"按钮的界面下启动, 游戏中大部分界面都有这个按钮, 除了主界面本身, 后宅, 指挥喵.
## Why Alas lost connection when I start another emulator 为什么我打开另一个模拟器时Alas会断开连接
Because you have 2 ADB in different versions.
Different version of ADB will kill each other when starting. Chinese emulators (NoxPlayer, LDPlayer, MemuPlayer, MuMuPlayer) use their own adb, instead of the one in system PATH, so when they start they kill the adb.exe Alas is using. There are 2 ways to solve this:
- Update both your emulator and your ADB to the latest.
If you install Alas by Easy_install, update the ADB in `<your_alas_install_folder>\python-3.7.6.amd64\Lib\site-packages\adbutils\binaries` to the latest.
If you install Alas in the advanced way, update the ADB in your system PATH.
- Replace the ADB in your emulator with the one Alas is using.
If you install Alas by Easy_install, find your ADB in `<your_alas_install_folder>\python-3.7.6.amd64\Lib\site-packages\adbutils\binaries` . If you install Alas in the advanced way, find your ADB in system PATH. Then goto you emulator installation folder, replace them.
Take NoxPlayer as an example. There are 2 ADB in nox install folder, `adb.exe` and `nox_adb.exe`. Make backup for these 2 files and delete them. Copy two `adb.exe` to your nox install folder, and rename them to `adb.exe` and `nox_adb.exe`.
因为你有两个不同版本的ADB
不同版本的ADB之间会互相结束对方. 国产模拟器 (夜神模拟器, 雷电模拟器, 逍遥模拟器, MuMu模拟器) 都会使用自己的ADB, 而不会使用配置在环境变量中的ADB. 所以当它们启动时, 就会结束Alas正在使用的 adb.exe. 有两个方法解决这个问题:
- 将你的模拟器和你的ADB都更新到最新.
如果你使用傻瓜式安装包安装的Alas, 更新位于 `<你的Alas安装目录>\python-3.7.6.amd64\Lib\site-packages\adbutils\binaries` 下的ADB.
如果你使用的高级方法安装的Alas, 更新位于环境变量中的ADB.
- 将模拟器中的ADB替换为Alas使用的ADB.
如果你使用傻瓜式安装包安装的Alas, 找到位于 `<你的Alas安装目录>\python-3.7.6.amd64\Lib\site-packages\adbutils\binaries` 下的ADB. 如果你使用的高级方法安装的Alas, 找到位于环境变量中的ADB.
以夜神模拟器为例, 夜神模拟器安装目录下有两个ADB, `adb.exe``nox_adb.exe` 备份它们并删除. 复制两份 `adb.exe` 到夜神模拟器安装目录, 重命名为 `adb.exe``nox_adb.exe`.

View File

@ -1,15 +0,0 @@
# How to Update
Step 1. Run alas.bat
Step 2. Select
option 4 to select the updater
Step 3. Select which repo to pull from, if in doubt go with option 1.
This will update your ALAS.
If you do not have a alas.bat with the update option, please download a fresh copy from newest release.
# DO NOT ADD FILES MANUALLY INSIDE THE ALAS FOLDER, THIS WILL CAUSE CONFLICTS AND YOU WILL HAVE TO DOWNLOAD EVERYTHING AGAIN.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

View File

@ -1,289 +0,0 @@
# 傻瓜式安装教程
下面讲述如何使用傻瓜式安装包, 安装 Alas
1. **下载** 傻瓜式安装包
点击 [这里](https://github.com/whoamikyo/AzurLaneAutoScript/releases) , 找到最新的傻瓜式安装包
下载 `AzurLaneAutoScript.zip`, 不要下载 `Source code`.
![install_release](Installation_cn.assets/install_release.png)
2. **解压** 解压到目录, 比如解压到 `D:\AzurLaneAutoScript`, 请确保有至少 1.5 GB 的空间.
你会看到有一个名为 `toolkit` 的目录, 和一个名为 `Easy_Install-V2.bat` 的批处理文件
![folder_unzip](Installation_cn.assets/folder_unzip.png)
3. **右键 Easy_Install-V2.bat, 选择"以管理员身份运行"**
当看到这些提示时, 说明已经获取到管理员权限, 按下回车键继续
![install_admin](Installation_cn.assets/install_admin.png)
这下面这个界面, 输入 `gitee` , 也就是从选择码云(gitee)镜像下载, 然后按下回车键
![install_batch](Installation_cn.assets/install_batch.png)
当看到如下信息时, 说明安装成功
![install_batch_finish](Installation_cn.assets/install_batch_finish.png)
再次按下回车, 就会回到标题界面
然后你就可以关掉这个窗口了
4. **运行 alas.bat**
在安装完成后, 你会看到刚才的目录下多出很多文件和文件夹
![folder_install_finish](Installation_cn.assets/folder_install_finish.png)
现在右键点击 alas.bat, 选择 "**以管理员身份运行**"
你将会看到一个黑色的命令行窗口
![alas_adb](Installation_cn.assets/alas_adb.png)
在第一次运行的时候, 要求输入模拟器的SERIAL
以下是一些常见模拟器的默认SERIAL. (如果你有多个模拟器实例, 它们的serial可能不是这里列出的)
| 模拟器 | Emulator | serial |
| ---------- | ---------- | --------------- |
| 蓝叠模拟器 | BlueStacks | 127.0.0.1:5555 |
| 夜神模拟器 | NoxPlayer | 127.0.0.1:62001 |
| MuMu模拟器 | MuMuPlayer | 127.0.0.1:7555 |
| 逍遥模拟器 | MemuPlayer | 127.0.0.1:21503 |
| 雷电模拟器 | LDPlayer | emulator-5554 |
输入完成后, 按下回车
你会看到一些一闪而过的信息
![alas_u2_init](Installation_cn.assets/alas_u2_init.png)
接下来, 进入到主界面
![alas_main](Installation_cn.assets/alas_main.png)
按下数字2, 也就是选择国服, 然后回车
然后 Alas 的 GUI 就出来了
![alas_gui](Installation_cn.assets/alas_gui.png)
5. **检查模拟器连接**
点击左侧菜单的 `设备设置`,
- 把修改`设备`修改成刚刚使用的SERIAL.
- 检查`包名`, `com.bilibili.azurlane` 是碧蓝航线B服的APK包名 (Package Name).
如果你玩的是碧蓝航线B服, 不需要修改.
如果你玩的是渠道服, 请自行搜索对应的APK包名并修改.
- 点击`开始`, 这将保存选项并检查连接, 然后点击`编辑`返回主界面
![alas_emulator_settings](Installation_cn.assets/alas_emulator_settings.png)
当你看到以下信息时, 说明一切正常.
![alas_emulator_save](Installation_cn.assets/alas_emulator_save.png)
现在可以点击`编辑`返回主界面, 开始使用Alas了.
# 傻瓜式更新教程
右键点击 alas.bat, 选择 "**以管理员身份运行**", 进入 Alas.bat 的主界面.
![alas_main](Installation_cn.assets/alas_main.png)
按下数字4, 也就是更新, 再按下回车键.
这里会询问更新的方式.
![alas_update_method](Installation_cn.assets/alas_update_method.png)
一般选 1 , 也就是覆盖已有文件(会保留用户设置). 如果你手动修改了一些文件, 比如编写了自己的地图逻辑, 并且希望保留, 就选 2.
输入选择的数字, 然后按下回车键.
进入到选择界面
![alas_update](Installation_cn.assets/alas_update.png)
输入数字4, 选择从gitee镜像更新. 如果 Alas 有大版本更新需要更新, 就输入数字 5, 更新运行环境.
然后按下回车键
看到以下信息时, 说明更新完成
![alas_update_finish](Installation_cn.assets/alas_update_finish.png)
再次按下回车, 就会回到标题界面
然后你就可以关掉这个窗口了
# 高级用户安装教程
> 这里的高级用户, 指对python有一定了解, 能使用搜索引擎独立解决问题的人.
> 傻瓜式安装包里有什么?
>
> [WinPython](https://winpython.github.io/) Winpython64-3.7.6.0dot
>
> 预安装的依赖
>
> [PortableGit](https://git-scm.com/download/win)
>
> Easy_Install-V2.bat, 用来执行 git pull
- Clone 本项目
### 创建虚拟环境
虚拟环境中的 python 版本必须为 3.7.6
激活虚拟环境, 并安装依赖
```
pip install -r requirements.txt
```
> 如果在安装 Python-Levenshtein 的时候遇到 Microsoft Visual C++ 14.0 is required, 可以在[这里](https://www.lfd.uci.edu/~gohlke/pythonlibs/)下载它的 wheel 文件, 然后用 pip 安装
>
> ```
> pip install python_Levenshtein-0.12.0-cp37-cp37m-win_amd64.whl
> ```
### 安装模拟器
| 设备 | Device | 模拟器版本 | 安卓版本 | adb截图 | u2截图 | adb点击 | u2点击 |
| ---------- | ---------- | ---------- | -------- | ------- | ------ | ------- | ------ |
| 逍遥模拟器 | MemuPlayer | 7.1.3 | 5.1.1 | 0.308 | 0.275 | 0.294 | 0.146 |
| 雷电模拟器 | LDPlayer | 3.83 | 5.1.1 | 0.329 | 0.313 | 0.291 | 0.146 |
| 夜神模拟器 | NoxPlayer | 6.6.0.0 | 5.1.1 | 0.339 | 0.313 | 0.505 | 0.141 |
| MuMu模拟器 | MuMuPlayer | 2.3.1.0 | 6.0.1 | 0.368 | 0.701 | 0.358 | 0.148 |
| 一加5 | Oneplus5 | | 7.1.1 | 1.211 | 0.285 | 0.447 | 0.160 |
这里给出了一些常见模拟器的性能测试结果, 测试平台 Windows 10, I7-8700k, 1080ti, nvme SSD, 模拟器分辨率1280x720, 碧蓝航线 60帧开启, 进入地图 7-2, 执行100次取平均, 单位秒.
2020.06, 对 [aScreenCap](https://github.com/ClnViewer/Android-fast-screen-capture) 和 蓝叠模拟器(BlueStacks) 追加测试.
| 设备 | Device | 模拟器版本 | 安卓版本 | aScreenCap截图 | adb截图 | u2截图 | adb点击 | u2点击 |
| ---------------- | --------------- | ------------ | -------- | -------------- | ------- | ------ | ------- | ------ |
| 夜神模拟器 | NoxPlayer | 6.6.0.0 | 5.1.1 | 0.242 | 0.408 | 0.368 | 0.532 | 0.142 |
| 蓝叠模拟器(台湾) | BlueStacks (TW) | 4.205.0.1006 | 5.1.1 | 0.129 | 0.280 | 0.458 | 0.234 | 0.151 |
> **结论**
>
> 推荐使用 蓝叠模拟器国际版
>
> 推荐使用 aScreenCap 截图, uiautomator2点击
> 由于海图识别模块对截图质量有很高的要求, `AzurLaneAutoScript` 暂时不支持手机, 必须使用模拟器. (Alas其实是支持手机的, 远古版本的Alas也是在手机上测试的, 但是长时间运行会发热和假死, 就放弃了)
- 安装一款安卓模拟器
- 模拟器分辨率设置为 `1280x720` .
### 配置ADB
- 获取 [ADB](https://developer.android.com/studio/releases/platform-tools)
- 将ADB配置于系统的环境变量中, 并测试是否配置成功.
```
adb devices
```
### 安装 uiautomator2
> [uiautomator2](https://github.com/openatx/uiautomator2), 是一个自动化测试的库, 可以加快截图和点击的速度. `AzurLaneAutoScript` 也可以使用ADB来执行截图和点击, 就是慢一点而已.
- 执行
```
python -m uiautomator2 init
```
这会在所有连接的设备上安装 [uiautomator-server](https://github.com/openatx/android-uiautomator-server/releases) , [atx-agent](https://github.com/openatx/atx-agent), [minicap](https://github.com/openstf/minicap), [minitouch](https://github.com/openstf/minitouch) . 如果设备是模拟器, uiautomator2 将跳过 minicap 的安装.
- 检查 uiautomator2 是否安装成功
修改 `module.dev_tools` 下的 `emulator_test.py` 中的 `SERIAL`, 并在虚拟环境下执行
```
python -m dev_tools.emulator_test
```
一些模拟器的默认 serial:
如果你有多个模拟器, 需要执行 `adb devices` 查看对应的 serial
| 设备 | Emulator | serial |
| ---------- | ---------- | --------------- |
| 蓝叠模拟器 | BlueStacks | 127.0.0.1:5555 |
| 夜神模拟器 | NoxPlayer | 127.0.0.1:62001 |
| MuMu模拟器 | MuMuPlayer | 127.0.0.1:7555 |
| 逍遥模拟器 | MemuPlayer | 127.0.0.1:21503 |
| 雷电模拟器 | LDPlayer | emulator-5554 |
在运行时, 会打印当前截图方式的平均耗时和耗时标准差.
打印几个之后, 就说明没问题, 你可以结束它.
> **不同截图方式的差别**
>
> aScreenCap 远快于 uiautomator2 和 ADB. 但是cpu占用高, 在少部分模拟器上运行会报错.
>
> uiautomator2 在 VirtualBox 内核的模拟器上 (夜神模拟器, 逍遥模拟器, 雷电模拟器), 快于ADB, 但是在 BlueStacks 内核的模拟器上 (蓝叠模拟器, MuMu模拟器), 慢于 ADB.
>
> ADB 兼容性最好.
>
> Alas 默认使用 ADB 截图, 如果 aScreenCap 可用, 则推荐使用.
> **不同点击方式的差别**
>
> uiautomator2 使用的 minitouch 在点击速度, 手势操作等方面碾压 ADB.
>
> Alas 默认使用 uiautomator2 点击, 也推荐使用 uiautomator2.
### 启动
- 快捷方式运行
右键点击 `alas.pyw`, 创建快捷方式
右键点击刚刚创建快捷方式, 点击`属性`, 将`目标` 更改为
```
"<你的虚拟环境的绝对路径>\pythonw.exe" "<你的Alas安装目录的绝对路径>\alas_cn.pyw"
```
点击 `确定`
现在, 你可以把这个快捷方式移动到任意地方, 比如桌面
> 这个启动方式是最好的, 不会像使用alas.bat那样出现命令行窗口, 双击即可运行
>
> 但是如果 GUI 启动中出现错误, 不会出现任何窗口, 也不会有任何报错信息
- 多开运行
复制 alas.pyw, 并重命名. 首次运行时会复制template.ini的设置. 脚本运行时会使用同名的ini配置文件.
比如, 将`alas.pyw`复制为`alas2.pyw`, 创建快捷方式运行, 在运行时就会使用`config\alas2.ini`
- 其他运行方式 (不推荐)
- 通过命令行运行. 虽然alas使用了 [Gooey](https://github.com/chriskiehl/Gooey), 一个将命令行转为GUI的库, 但是Alas并不是先有命令行方法运行再用gooey的, Alas是为了使用gooey快速编写GUI而去拼凑命令行参数的. 因此使用命令行会很难受.
- 修改配置文件 `config/alas.ini` , 在 `alas.py` 中调用相关函数

View File

@ -1,127 +0,0 @@
# Quick guide
#### Requirements
* A good PC (Older or underpowered PCs may have problems running the emulator correctly)
* [Python](https://www.python.org/ftp/python/3.7.6/python-3.7.6-amd64.exe) installed and added to PATH (is highly recommended 3.7.6 64bit version only)
* Latest [ADB](https://developer.android.com/studio/releases/platform-tools) added to PATH.
* [I don't know how to add to PATH](https://www.youtube.com/watch?v=Y2q_b4ugPWk)
* The use of a virtual environment (venv) in python is highly recommended
* ADB debugging enabled and emulator with 1280x720 resolution
* **Read the entire guide before asking any questions.**
# New automatic installation method (Recommended)
* [Video tutorial](https://www.youtube.com/watch?v=bp4kd8P1qT4)
* Download the zip [AzurLaneAutoScript.zip
](https://github.com/whoamikyo/AzurLaneAutoScript/releases) (right-click, save)
* Extract and run **Easy_Install-V2.bat** (ex. `C:\`, or `D:\`, any will work). The installer will create the nessesary folders.
* **Do not run the installer in a folder that contains spaces in the name, this will make the scripts not work correctly, create a folder at the root of some drive like `C:\ALAS\` or `D:\ALAS\`, then place Easy_Install-V2.bat in that folder and run if you must run it inside of a folder.**
* To Run ALAS, just click in `alas.bat` inside the installation folder and follow the instructions located below in the `How to use Use` section.
* To avoid connection errors it's recommended to configure your emulator port correctly, in `alas.bat` first start will be prompt to enter your HOST:PORT, below you have more information on how to find your emulator PORT, READ ALL.
# Manual installation method (Not Recommended)
### How to create e python virtual environment
* First install [Python](https://www.python.org/ftp/python/3.7.6/python-3.7.6-amd64.exe)
* Create a folder where you will put the virtual environment, I recommend creating a folder `venv` in the project's root directory
* Go to project root in command line
* type `python -m venv path_to_your_folder\venv`
![venv](quickguide.assets/venv.png)
You can see that now python has created some folders and files in the venv folder, it has created a completely clean virtual environment, thus preventing any conflicts.
* Now, it is necessary to activate the virtual environment in command line, go to project root (the same where you have the file alas.py) and type `.\venv\scripts\activate.bat`
![venv_activate](quickguide.assets/venv_activate.png)
Look that a `(venv)`, with that we know that we are in a virtual environment.
If you type `pip list` should get this output:
![pip_list](quickguide.assets/pip_list.png)
Now, you can proceed with the installation of the requirements through `pip install -r requirements.txt`
#### Installation
* Clone this repository
* Install the requirements.txt (`pip install -r requirements.txt`)
* Install an android emulator (Tested on BlueStacks)
* The android emulator resolution must be set to `1280x720`
* Test if the ADB is working correctly `adb devices`
The output must be something like this
![ADB](quickguide.assets/adb_test.png)
Test if your Python is working correctly `python --version`
![Python](quickguide.assets/python_test.png)
* Install uiautomator2
uiautomator2, is an automated test library that can speed up screenshots and clicks. You can also use ADB to perform screenshots and clicks, but it is a slower way.
For performance optimization, it is recommended to use ADB screenshots, uiautomat2 screenshot slightly faster than adb screenshot, but cpu consumption double.
* Perform
`python -m uiautomator2 init`
The output must be something like this:
![U2](quickguide.assets/u2_test.png)
(in this case, I had already installed)
* Check if uiautomator2 was installed successfully
Modify the `serial` in \dev_tools\emulator_test.py line 31 and, execute from root project directory (the same where you have the file alas.py)
`python -m dev_tools.emulator_test`
The output must be something like this:
![emulator_test](quickguide.assets/emulator_test.png)
The default `serial` for some emulators:
| Android Emulator | serial |
|------------------|-----------------|
| NoxPlayer | 127.0.0.1:62001 |
| MuMuPlayer | 127.0.0.1:7555 |
| Bluestacks | 127.0.0.1:5555 |
| LDPlayer | emulator-5554 |
You can check a new app installed in your emulator:
![emulator_test](quickguide.assets/atx.png)
If you open up can check if are running:
![emulator_test](quickguide.assets/atx_running.png)
If are not running, you cannot use U2 and will get error.
## How to use Use
If you installed with `Easy_Install-V2.bat` you can just double-click `alas.bat` located in your `AzurLaneAutoScript` folder. It will ask you for your emulator connection information (located in the table above). Once you've entered the serial information, choose your preferred language.
If not you can call `py alas_en.pyw` from command line to open the GUI
Multi-usage: copy alas.pyw, and rename, double-click run on it. The settings of template.ini are copied when the first run runs. The script runtime uses the ini profile of the same name.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

After

Width:  |  Height:  |  Size: 102 KiB

View File

@ -1,7 +1,3 @@
# Documentation
Here are some documents to help both beginners and advanced users.
Before asking any questions, make sure that your question has not been answered in any document.
Feel free to add new information through the pull request.
Documentation has moved to https://github.com/LmeSzinc/AzurLaneAutoScript/wiki

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 414 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 229 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 415 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 300 KiB

View File

@ -1,118 +0,0 @@
# Bugs in AzurLane
In CN, people found so many bugs in AzurLane.
Here list the bugs which are found by Alas or have negative effects on Alas.
## Mood update bug
> Appear time: Since game Launched
>
> Found time: Long time ago
>
> Fixed time: Not yet
AzurLane server and Alas calculate mood correctly, but the game don't.
After a long run, you will see red face in game, but actually is in high morale
So need to restart the game to update mood from server.
Detailed explanation (In Chinese), [here](https://www.bilibili.com/read/cv5088868)
> **心情与后宅大讲堂系统讲解和奇怪操作**
>
> **Strange things in dorm, lecture hall and mood**
>
> 瓜游的很多数据并不是在本地运算的,你每个舰娘实际的心情都是存在服务器里的,并且只有在你重新登录游戏的时候才会刷新本地数据。。。而你出击能否获取额外经验和加不加好感度实际上也是服务器端决定的,本地数据只是让你看看的而已,表面上这其实没啥问题,毕竟瓜游心情机制其实属于一个很傻瓜的算法,所以确实也没必要每次出击都重新获取一次数据。
>
> 但是问题来了你本地端的算法里认为后宅2楼和后宅1楼的心情回复是相同的。而服务器端认为2楼相对于1楼有额外10点每小时的心情回复。这就导致你后宅2楼的舰娘每小时实际心情都会比本地端显示的高10点有人提到本地端单次回复的心情值没问题可能这个bug的表现形式单纯的只是漏回复了心情举个极端例子你连续以一个正好的速度带着2楼舰娘恒定肝15个小时期间始终没有重启游戏那么你本地端会显示你2楼舰娘心情为0并且会提示你心情0出击会扣好感但是实际上服务器端里你舰娘的心情是满值的150点即使你强制出击也不会扣好感度因为决定好感度增减与否也是服务器端决定的而服务器端里的心情是150自然不会扣好感。
>
> 怎么解决这个问题呢目前只有1个办法重启游戏。这样才会重新获取一次心情数据。
>
> 总之2楼舰娘你保持每小时出击25次以内也就是每2.4分钟才出击一次就能始终保持20%心情buff如果是婚舰那就是每2分钟出击1次。你实在觉得那个错位的心情提示很难受的话没事重启一下游戏就好了。
## Commission list refresh bug
> Appear time: Since game Launched
>
> Found time: Long time ago
>
> Fixed time: Not yet
If you don't restart the game and receive a commission in new day, the extra daily commission will be preserved, these commissions are time-wasting and low cost-effective.
This is a bug in game, and if you make good use that, can turn out to be a feature.
## Level number position bug on married PRY ship
> Appear time: Since New-UI launched
>
> Found time: 2019.07
>
> Fixed time: 2019.11
I found this, because the OCR for the level of exercises opponents don't work well.
> 未婚金科研, 已婚金科研, 未婚彩科研, 都不会挡.
> 已婚彩科研会挡.
> (截图均来自演习对手, 船坞, 经验结算, 都会出现遮挡)
>
> 搜了一下, 2019-07-03 1661楼提过这个问题了, 两个月过去了, 并没有解决....
![2019-09-20_已婚彩科研框遮挡等级数字](bugs_in_azurlane_en.assets/bug_on_married_PRY_ship.png)
## Fleet ammo icon position bug
> Appear time: Unknown
>
> Found time: 2020.04.29, CN event 复刻苍红的回响
>
> Fixed time: Not yet, may be a feature
When fleet get caught by siren, the fleet ammo icon goes to the right.
> 被精英抓住后, 弹药图标会漂移到右边
![fleet_ammo_bug](bugs_in_azurlane_en.assets/fleet_ammo_bug.png)
## Cats buying button bug
> Appear time: Unknown
>
> Found time: 2020.05.05
>
> Fixed time: Not yet
When adding cats, only clicking the high-lighted area is valid.
> 指挥喵购买界面的加减按钮, 实际范围比显示的要小, 经常点半天没反应
> 只有点图中高亮的范围才有效
![cat_button_bug](bugs_in_azurlane_en.assets/cat_button_bug.jpg)
## Submarine zone icon bug
> Appear time: 2020.05.21, since CN event 穹顶下的圣咏曲
>
> Found time: 2020.05.29
>
> Fixed time: Not yet
When switching the submarine zone, the icon in the strategy don't change.
> 显示或隐藏潜艇狩猎范围, 图标都不会改变
![submarine_bug](bugs_in_azurlane_en.assets/submarine_bug.png)
## Submarine call icon not loaded in battle
> Appear time: Unknown
>
> Found time: 2020.07.01
>
> Fixed time: Not yet
After a few battle the icon for calling submarine may not be loaded correctly. If you click on it, it back to normal, if you don't, this bug will still appear in next battle.
![combat_submarine_icon_bug](bugs_in_azurlane_en.assets/combat_submarine_icon_bug.png)

View File

@ -1,143 +0,0 @@
# How to debug a perspective error
## Normal logs
This is an example log.
```
2020-06-03 00:44:46.221 | INFO | vanish_point: ( 646, -1736)
2020-06-03 00:44:46.222 | INFO | distant_point: (-2321, -1736)
2020-06-03 00:44:46.266 | INFO | 0.235s _ Horizontal: 5 (7 inner, 3 edge)
2020-06-03 00:44:46.266 | INFO | Edges: / \ Vertical: 9 (10 inner, 3 edge)
2020-06-03 00:44:46.273 | INFO | Center grid: (3, 1)
2020-06-03 00:44:46.493 | INFO | -- -- -- -- -- 2M -- --
2020-06-03 00:44:46.501 | INFO | MY -- -- MY -- -- 3M --
2020-06-03 00:44:46.501 | INFO | -- -- FL -- -- -- -- --
2020-06-03 00:44:46.501 | INFO | -- 1L -- MY -- 2L --
```
## Too few grid lines
This may happens when it detected too few grid lines.
``` File "E:\ProgramData\Pycharm\AzurLaneAutoScript\module\map\camera.py", line 114, in update
File "AzurLaneAutoScript\module\map\camera.py", line 114, in update
self.grids = Grids(self.device.image, config=self.config)
File "AzurLaneAutoScript\module\map\grids.py", line 19, in __init__
super().__init__(image, config)
File "AzurLaneAutoScript\module\map\perspective.py", line 81, in __init__
self.crossings = self.horizontal.cross(self.vertical)
File "AzurLaneAutoScript\module\map\perspective_items.py", line 170, in cross
points = np.vstack(self.cross_two_lines(self, other))
File "lib\site-packages\numpy\core\shape_base.py", line 234, in vstack
return _nx.concatenate([atleast_2d(_m) for _m in tup], 0)
File "lib\site-packages\numpy\core\shape_base.py", line 234, in <listcomp>
return _nx.concatenate([atleast_2d(_m) for _m in tup], 0)
File "AzurLaneAutoScript\module\map\perspective_items.py", line 163, in cross_two_lines
for rho1, sin1, cos1 in zip(lines1.rho, lines1.sin, lines1.cos):
AttributeError: 'Lines' object has no attribute 'rho'
```
```
File "AzurLaneAutoScript\module\map\camera.py", line 114, in update
self.grids = Grids(self.device.image, config=self.config)
File "AzurLaneAutoScript\module\map\grids.py", line 19, in __init__
super().__init__(image, config)
File "AzurLaneAutoScript\module\map\perspective.py", line 98, in __init__
self.horizontal, inner=inner_h.group(), edge=edge_h)
File "AzurLaneAutoScript\module\map\perspective.py", line 352, in line_cleanse
clean = self.mid_cleanse(origin, is_horizontal=lines.is_horizontal, threshold=threshold)
File "AzurLaneAutoScript\module\map\perspective.py", line 346, in mid_cleanse
mids = convert_to_y(mids)
File "AzurLaneAutoScript\module\map\perspective.py", line 277, in convert_to_y
return Points([[x, self.config.SCREEN_CENTER[1]] for x in xs], config=self.config) \
File "AzurLaneAutoScript\module\map\perspective_items.py", line 15, in __init__
self.x, self.y = self.points.T
ValueError: not enough values to unpack (expected 2, got 0)
```
Try reduce the threshold of `cv2.HoughLines`. Default is 75.
Lower threshold means more lines.
```
INTERNAL_LINES_HOUGHLINES_THRESHOLD = 40
EDGE_LINES_HOUGHLINES_THRESHOLD = 40
```
Then you should also lower this and make a closer fit to the lines.
Lower means closer fit, ignore more wrong lines.
```
COINCIDENT_POINT_ENCOURAGE_DISTANCE = 1.5
```
## Camera outside map
```
File "AzurLaneAutoScript-master\module\map\camera.py", line 114, in update
self.grids = Grids(self.device.image, config=self.config)
File "AzurLaneAutoScript-master\module\map\grids.py", line 19, in __init__
super().__init__(image, config)
File "AzurLaneAutoScript-master\module\map\perspective.py", line 98, in __init__
self.horizontal, inner=inner_h.group(), edge=edge_h)
File "AzurLaneAutoScript-master\module\map\perspective.py", line 383, in line_cleanse
raise PerspectiveError('Camera outside map: to the %s' % ('upper' if lines.is_horizontal else 'right'))
module.exception.PerspectiveError: Camera outside map: to the upper
```
Alas can not handle if camera is not focusing on any map grid, it will swipe back if catches `Camera outside map`. This may happens when some inner lines are detected as edge lines.
Try adjust the parameter of `scipy.signal.find_peaks`, the `height` value.
- **height** Lower means detect lighter lines, 255 means pure black.
- **width** Line width, in pixels.
- **prominence** Line needs to be how much darker than surrounding pixels.
- **distance** Minimum distance between two detected point on line.
- **wlen** Maximum amount of data to be processed in one time.
Know more about these parameters [here](https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.find_peaks.html)
```
INTERNAL_LINES_FIND_PEAKS_PARAMETERS = {
'height': (150, 255 - 24),
'width': (0.9, 10),
'prominence': 10,
'distance': 35,
}
EDGE_LINES_FIND_PEAKS_PARAMETERS = {
'height': (255 - 24, 255),
'prominence': 10,
'distance': 50,
'width': (0, 10),
'wlen': 1000
}
```
## Coincident point unexpected
```
2020-05-31 20:04:47.397 | INFO | Horizontal coincident point unexpected: [-92.817316 141.99589405]
2020-05-31 20:04:48.509 | INFO | Vertical coincident point unexpected: [-692.01967461 141.68981244]
```
Try adjust the initial value of coincident point. if 141.99589405 is in (129 - 3, 129 + 3), it shut up.
But remember, an incorrect value will ruin everything in map detection, it usually works fine with these logs show up.
```
MID_DIFF_RANGE_H = (129 - 3, 129 + 3)
MID_DIFF_RANGE_V = (129 - 3, 129 + 3)
```
## Too many deleted lines

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 249 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

View File

@ -1,455 +0,0 @@
# 参与开发 Development
- 如何添加一个按钮 How to add a button
- 如何适配一张新的地图 How to adapt to a new map
- 如何支持其他服务器/语言 How to support other server/language
## 如何添加一个按钮 How to add a button
按钮文件按模块保存于 `./asset` 目录下, 按钮定义于每个模块的 `asset.py` 文件中.
比如 `BATTLE_PREPARATION` 按钮文件是这样的
![BATTLE_PREPARATION](development.assets/BATTLE_PREPARATION.png)
`asset.py` 它是这样的
```
BATTLE_PREPARATION = Button(area=(1043, 607, 1241, 667), color=(234, 179, 97), button=(1043, 607, 1241, 667), file='./assets/combat/BATTLE_PREPARATION.png')
```
注意, 所有的 `asset.py` 都是由 `./dev_tools/button_extract.py` 生成的, 不要手动去修改. 手动填写按钮的四角坐标是一件非常痛苦的事, 特别是当有大量的按钮或者按钮需要频繁修改的时候.
假设我们希望添加一个 `确定` 按钮, 它出现于潜艇信号扫描时.
1. **截图**
![screenshot](development.assets/screenshot.png)
2. **将图片复制到./asset下相应的目录中**, 更改文件名, 比如 `SEARCH_CONFIRM.png`
3. **拖动至Photoshop中**, 这里以 Photoshop CS6 为例
4. **使用选区工具框选按钮区域**
5. **播放动作**
第一次操作时, 可以按照以下步骤添加动作.
在菜单栏的 `窗口` 中, 点击 `动作` , 弹出动作窗口
在添加动作之前, 最好备份当前图片, 因为接下来需要记录的操作是不可逆的.
- 在动作窗口中, 点击新建动作的图标, 按照自己的喜好命名, 比如 `button_image`. 点击 `记录` , 注意灰色的圆圈变红了, 这表示动作录制开始了
- 在图片区域单击鼠标右键, 点击 `选择反向`
- 在菜单栏的 `编辑` 中, 点击 `填充`
- 在弹出的填充选项窗口中, 填充内容使用 `黑色` , 填充模式选择 `正常`, 不透明度选择 `100`, 点击 `确定`
- 在菜单栏的 `文件` 中, 点击 `保存`
- 在菜单栏的 `文件` 中, 点击 `关闭`
- 在动作窗口中, 单击停止录制的图标, 此时动作录制停止
录制完成后, 会得到动作如下
```
button_image
反向
填充
使用:黑色
不透明度:100%
模式:正常
储存
关闭
```
在以后的添加按钮的时候, 就可以直接点击 播放动作 的按钮, 完成图片处理
6. **(可选) 添加属性覆盖图片**
一个按钮具有三个属性:
- area, 按钮识别的区域
- color, 按钮的颜色
- button, 按钮出现后的点击区域
假如添在同一目录下放置图片文件 `SEARCH_CONFIRM.BUTTON.png` , 并按照刚才描述的方法处理图片. 那么这张图片的 `button` 属性将覆盖 `SEARCH_CONFIRM.png``button` 属性.
这是一个非常有用的特性, 因为脚本通常需要判断截图中出现的元素, 然后点击按钮, 需要判断的地方和需要点击的地方可能不出于同一位置.
7. **运行./dev_tools下的button_extract.py**
button_extract.py会自动提取按钮的属性, 免去了人工输入的烦恼
```
python -m dev_tools.button_extract
```
8. **使用按钮**
继承 module.base.base 下的 ModuleBase 类, 可以调用以下方法:
- appear()
- appear_then_click()
- wait_until_appear()
- wait_until_appear_then_click()
- wait_until_disappear()
方法的可选参数:
- offset (bool, int, tuple): 默认为0, 为0时将使用按钮在截图上的平均颜色识别按钮
输入后, 表示按钮参照预设的区域偏移的范围, 此时使用模板匹配识别按钮
- interval (int): 按钮的触发间隔, 默认为0.
代表按钮的触发间隔, 当按钮点击后有动画时, 这个参数可以防止按钮被频繁地点击.
- screenshot (bool): 按钮出现后保存截图
- genre (str): 截图保存的子目录名称
## 如何制作用于敌人识别的模板图片 How to make a template image for enemy detection
首先, 我们不能直接裁切截图来制作模板图片, 因为地图中的物体是有透视的. 我们需要使用 `dev_tools/relative_crop.py` 来获取图片. `get_relative_image` 可以根据透视裁剪出相对位置的图片, 并放大到固定的大小.
下图展示了`self.get_relative_image((-1, -1, 1, 0), output_shape=(120, 60))`的裁切区域
![relative_crop](development.assets/relative_crop.png)
运行 `dev_tools/relative_crop.py` 后, 会得到大量的临时图片, 找到对应格子的图片, 在图片中裁切出需要的模板.
将模板图片放置于 `assets/<server>/template` 目录下, 文件名需以 `TEMPLATE_` 开头, 最后运行 button_extract.
## 如何适配一张新的地图 How to adapt to a new map
> 以下内容已过时, 但你仍然可以依照这些内容创建新的地图
>
> 现在可以使用 `./dev_tools/map_extractor.py` 自动生成地图文件, 详细说明在该文件的末尾.
下面举例适配 7-2 的简单版, 完整逻辑在 campaign.campaign_main.campaign_7_2
### 定义地图
1. **新建**
`./campaign` 下新建目录, 如果是新的活动图, 建议按照 `event_<活动时间>_<服务器>` 命名目录, 比如 event_20200326_cn.
新建 .py 文件, 文件名称为地图名, 小写, 以字母开头, 比如sp3, d3.
2. **导入**
```
from module.campaign.campaign_base import CampaignBase
from module.map.map_base import CampaignMap
```
3. ```
MAP = CampaignMap()
```
4. **(可选) 设置地图大小**
不填时, 根据海域信息生成
```
MAP.shape = 'H5'
```
5. **设置海域信息**
`碧蓝航线WIKI` 中查看敌人刷新信息, 如果是新地图, WIKI可能需要一两天的时间更新.
![wiki_7_2](development.assets/wiki_7_2.jpg)
```
MAP.map_data = '''
ME ++ ME -- ME ME -- SP
MM ++ ++ MM -- -- ME --
ME -- ME MB ME -- ME MM
-- ME -- MM -- ME ++ ++
SP -- ME ME -- ME ++ ++
'''
```
地图信息最少要包含海陆位置, 识别海陆现在还有一些困难
如果出生点比较偏僻的话, 还应包括出生点
```
MAP.map_data = '''
-- ++ ++ -- -- -- -- SP
-- ++ -- -- -- -- -- --
-- -- -- -- -- -- -- --
-- -- -- -- -- -- ++ ++
SP -- -- -- -- -- ++ ++
'''
```
关于这些符号的含义, 在 module.map.grid_info.py 中.
| print_name | property_name | description |
|------------|----------------|-------------------------|
| ++ | is_land | fleet can't go to land |
| -- | is_sea | sea |
| __ | | submarine spawn point |
| SP | is_spawn_point | fleet may spawns here |
| ME | may_enemy | enemy may spawns here |
| MB | may_boss | boss may spawns here |
| MM | may_mystery | mystery may spawns here |
| MA | may_ammo | fleet can get ammo here |
| MS | may_siren | Siren/Elite enemy spawn |
6. **(可选) 设置海域权重**
越高的数值表示越不希望舰队前往. 不设置时, 全部为10.
```
MAP.weight_data = '''
40 30 30 30 30 30 30 30
20 20 20 20 20 20 20 20
10 10 10 10 10 10 10 10
20 20 20 20 20 20 20 20
30 30 30 30 30 30 30 30
'''
```
7. **(可选) 设置相机位置**
```
MAP.camera_data = ['D3']
```
不设置时, 会根据地图大小和相机视野生成.
手动设置可以加快地图扫描的速度, 比如在 7-2 中, 自动生成的相机位置是 D2 D3 E2 E3, 实际上只需要将相机对准D3就可以看到整个地图(A1就不管了).
8. **(可选) 设置敌人刷新信息**
```
MAP.spawn_data = [
{'battle': 0, 'enemy': 3},
{'battle': 1, 'enemy': 2, 'mystery': 1},
{'battle': 2, 'enemy': 2, 'mystery': 1},
{'battle': 3, 'enemy': 1, 'mystery': 2},
{'battle': 4, 'enemy': 1},
{'battle': 5, 'boss': 1},
]
```
这里记录了每一战过后会刷新一些什么, 这个信息在 WIKI 上没有, 需要手动收集.
理论上, 不填写也可以运行. 敌人刷新信息的作用是纠正识别错误和缺失, 还有就是捕捉BOSS刷新时的相机移动
9. **展开地图**
```
A1, B1, C1, D1, E1, F1, G1, H1, \
A2, B2, C2, D2, E2, F2, G2, H2, \
A3, B3, C3, D3, E3, F3, G3, H3, \
A4, B4, C4, D4, E4, F4, G4, H4, \
A5, B5, C5, D5, E5, F5, G5, H5, \
= MAP.flatten()
```
这是为后续编写索敌逻辑做的准备, 可以使用以下代码生成
```
shape = 'H5'
def location2node(location):
return chr(location[0] + 64 + 1) + str(location[1] + 1)
def node2location(node):
return ord(node[0]) % 32 - 1, int(node[1]) - 1
shape = node2location(shape.upper())
for y in range(shape[1]+1):
text = ', '.join([location2node((x, y)) for x in range(shape[0]+1)]) + ', \\'
print(text)
print(' = MAP.flatten()')
```
10. **(可选) 定义地图元素**
比如定义路障
```
ROAD_MAIN = RoadGrids([A3, [C3, B4, C5], [F1, G2, G3]])
```
### 设置地图参数
地图参数将覆盖默认参数和用户参数, 具有最高优先.
如何不知道如何设置, 跳过即可. 但需要有定义.
```
class Config:
pass
```
可以设置的属性参照 module.config.config
比如, 对于微层混合D3, 因为地图加入了海雾, 需要不同的网格识别参数(默认参数是针对7-2的).
```
class Config:
INTERNAL_LINES_FIND_PEAKS_PARAMETERS = {
'height': (100, 220),
'width': 1,
'prominence': 10,
'distance': 35,
}
EDGE_LINES_FIND_PEAKS_PARAMETERS = {
'height': (255 - 80, 255),
'prominence': 2,
'distance': 50,
'wlen': 1000
}
```
对于活动图应该有以下设置, 开启精英识别.
```
class Config:
MAP_HAS_AMBUSH = False
MAP_HAS_FLEET_STEP = True
MAP_HAS_MOVABLE_ENEMY = True
MAP_HAS_SIREN = True
MAP_HAS_DYNAMIC_RED_BORDER = True
MAP_SIREN_COUNT = 2
```
如果前面仅输入了最少的海域信息, 或者没有敌人刷新信息, 应注明地图信息缺失, 此时会以开荒模式运行
```
class Config:
POOR_MAP_DATA = True
```
对于国服复刻苍红的回响, 抬高了相机位置, 网格缩小到0.66倍, 应该有
```
class Config:
MAP_GRID_CENTER_TOLERANCE = 0.3
INTERNAL_LINES_HOUGHLINES_THRESHOLD = 50
EDGE_LINES_HOUGHLINES_THRESHOLD = 50
CAMERA_SWIPE_MULTIPLY_X = 200 * 0.7
CAMERA_SWIPE_MULTIPLY_Y = 140 * 0.7
COINCIDENT_POINT_ENCOURAGE_DISTANCE = 1.
MID_DIFF_RANGE_H = (45, 70)
MID_DIFF_RANGE_V = (97 - 3, 97 + 3)
TRUST_EDGE_LINES = True
VANISH_POINT_RANGE = ((540, 740), (-4000, -2000))
DISTANCE_POINT_X_RANGE = ((-2000, -1000),)
INTERNAL_LINES_FIND_PEAKS_PARAMETERS = {
'height': (80, 255 - 40),
'width': (0.9, 10),
'prominence': 10,
'distance': 35,
'wlen': 100,
}
EDGE_LINES_FIND_PEAKS_PARAMETERS = {
'height': (255 - 40, 255),
'prominence': 10,
'distance': 50,
'wlen': 1000
}
```
### 编写索敌逻辑
- 可以不写, 全默认
```
class Campaign(CampaignBase):
MAP = MAP
```
- 简单的逻辑
按照第几战编写逻辑, 缺失时, 使用上一战的逻辑
一般而言, 每一战的逻辑应该以拣问号和清除路障开始, 再清除潜在的路障, 最后返回默认战斗逻辑
```
class Campaign(CampaignBase):
MAP = MAP
def battle_0(self):
self.clear_all_mystery(nearby=False)
if self.clear_roadblocks([ROAD_MAIN], strongest=True):
return True
if self.clear_potential_roadblocks([ROAD_MAIN], strongest=True):
return True
if self.clear_enemy(strongest=True, weight=True):
return True
return self.battle_default()
def battle_5(self):
self.clear_all_mystery(nearby=False)
if self.clear_roadblocks([ROAD_MAIN]):
return True
return self.fleet_2.brute_clear_boss()
```
未完待续
## 如何支持其他服务器/语言 How to support other server/language
### GUI
Copy `./module/config/argparser.py` to `argparser_xx.py` and change the argment.
Create a dictionary in `./module/config/dictionary.py` that translate your language to english.
Copy `alas_cn.py` to `alas_xx.py` and import `argparser_xx.py` . Then, edit server name.
> Format of .pyw file name: <sctipt_name>_<server_name>.pyw
>
> Script name is used to load ini file under `./config`, For example, alas_cn.pyw and alas_en.pyw both loads `./config/alas.ini`, but in different languages.
### Assets
Copy folder `./assets/cn` to `./assets/<your server>`, and replace the image. This will cost a lot of time to find, crop and test. Fortunately, if a image does not contain any charactors, it may works in all servers.
After replacing an image, don't forget to run `./dev_tools/button_extract.py`
### Class methods
Some method may be different in different servers. This decoractor is use to calls different function with a same name according to config (AzurLaneConfig instance).
```
from module.base.decorator import Config
from module.base.base import ModuleBase
class AnotherModule(ModuleBase):
@Config.when(SERVER='en')
def function(self):
# This method will be called only in EN server
pass
@Config.when(SERVER=None)
def function(self):
# This method will be called in other server
pass
```
### Other
There area also some modules difficult to change: the commission module.
In `./module/reward/commission.py`, I use [cnocr](https://github.com/breezedeus/cnocr) to recognize commission name in chinese, it may not works well in other languages.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

View File

@ -1,462 +0,0 @@
**Translation under progress**
# Participate in development
- How to add a button
- How to adapt to a new map
- How to support other server/language
## How to add a asset
Assets are stored in `./asset` directory, and is defined in each module `asset.py` file.
For example, the button `BATTLE_PREPARATION`
![BATTLE_PREPARATION](development.assets/BATTLE_PREPARATION_en.png)
In `asset.py` it is this:
```
BATTLE_PREPARATION = Button(area={'cn': (1043, 607, 1241, 667), 'en': (1045, 607, 1241, 667)}, color={'cn': (234, 179, 97), 'en': (235, 182, 102)}, button={'cn': (1043, 607, 1241, 667), 'en': (1045, 607, 1241, 667)}, file={'cn': './assets/cn/combat/BATTLE_PREPARATION.png', 'en': './assets/en/combat/BATTLE_PREPARATION.png'})
```
Note that all `asset.py` are generate with `./dev_tools/button_extract.py`, not to manually modify.
Suppose we want to add a `CONFIRM` button, which appears in the submarine signal scanning.
1. **Screenshot**
![screenshot](development.assets/ss.png)
the best way to take screenshots its using `adb exec-out screencap -p > screenshot.png`
If you get some output error, maybe you need to add your emulator port like that `adb -s 127.0.0.1:5555 exec-out screencap -p >screenshot.png`
2. **Copy the picture to the corresponding directory under `./asset`**, and change the file name, for example `EQUIP_CONFIRM.png`
3. **Drag to Photoshop**, here takes Photoshop CS6 as an example
4. **Use the selection tool (M) to select the button area**
5. **Play action**
In the first operation, you can follow the steps below to add actions.
In the menu bar `WINDOW`, click on `ACTION` option or ALT+F9
Before adding actions, it is best to back-up the current picture, because the next operation that needs to be recorded is irreversible.
* In the action window, click the icon of the new action, and name it according to your preferences, for example button_image. Click `Begin Recording`, note that the gray circle turns red, which means that action recording has started
* Right-click in the picture area, click `Inverse` or `SHIFT+CTRL+I`
* In the menu bar `Edit`, click `Fill` or `SHIFT+F5`
* In the pop-up fill window, fill content use `Black`, fill mode selection `normal`, opacity select 100, click `Ok`
* In the menu bar `File`, click `Save`
* In the menu bar `File`, click `Close`
* In the action window, click the stop recording icon, the action recording will stop.
After the recording is completed, you will get the following actions
```
button_image
Inverse
Fill
Using: Black
Opacity: 100%
Mode: normal
Save
Close
```
When adding assets in the future, you can directly click the button of the playback action to complete the image processing.
6. **(Optional) Add attributes to overlay pictures**
A asset has three attributes:
- area, the area identified by the asset
- color, the color of the asset
- button, click area after the asset appears
If you place `SEARCH_CONFIRM.BUTTON.png` in the same folder, and process it with the method mentioned above. The `button` attribute of `SEARCH_CONFIRM.BUTTON.png` will over-write `button` attribute of `SEARCH_CONFIRM.png`
This is a very useful feature, because the script usually needs to analyze the elements that appear in the screenshot and click on the button. The location that needs to be analyzed and the location that needs to be clicked may not be in the same position.
7. **Run button_extract.py under ./dev_tools**
button_extract.py will automatically extract the attributes of the button, eliminating the trouble of manual input.
```
python -m dev_tools.button_extract
```
8. **Use button**
Inherit the ModuleBase class under module.base.base, you can call the following methods:
- appear()
- appear_then_click()
- wait_until_appear()
- wait_until_appear_then_click()
- wait_until_disappear()
Optional parameters of the method:
- offset (bool, int, tuple): The default is 0, when it is 0, the average color of the button on the screenshot will be used to identify the button
After input, it means that the button refers to the preset area offset range, and then use the template matching to identify the button
- interval (int): The trigger interval of the button, the default is 0.
Represents the trigger interval of the button. When there is an animation after the button is clicked, this parameter can prevent the button from being frequently clicked.
- screenshot (bool): Save the screenshot after the button appears
- genre (str): the name of the subdirectory where the screenshot is saved
## How to make a template image for enemy detection
First of all, we can't simply crop the screenshot to make a template image, because things on the map have perspective. We should use `dev_tools/relative_crop.py` to get the image. `get_relative_image` can do a relative crop according to perspective and rescale to an output_size.
This image shows the cropping area of `self.get_relative_image((-1, -1, 1, 0), output_shape=(120, 60))`
![relative_crop](development_en.assets/relative_crop.png)
After running `dev_tools/relative_crop.py` , you will get a lot of temp images. Find the image of target grid, crop the template.
Paste the image under `dev_tools/relative_crop.py` , filename should start with `TEMPLATE_` , and run button_extract at last.
## How to adapt to a new map
> The following content is out-dated, but you can still create a map file according to this.
>
> Now you can auto-generate map files using `./dev_tools/map_extractor.py`, docs are at the end of the file.
The following example is adapted to the simple version of 7-2. The complete logic is in `campaign/campaign_main/7_2.py`
### Define the map
1. **New file**
In `./campaign`if it's a new event, create a new directory, as recommended name the directory `event_<eventdate>_<server>`, such as `event_20200326_cn `or `event_20200521_en`.
Create a new .py file, the file name is the map name, lowercase, starting with a letter, such as sp3, d3.
2. **Import**
```
from module.campaign.campaign_base import CampaignBase
from module.map.map_base import CampaignMap
```
3. ```
MAP = CampaignMap()
```
4. **(Optional) Set the map size**
When not filled, it is generated according to the sea area information
```
MAP.shape = 'H5'
```
5. **Set sea area information**
In Azur Lane WIKI you can see the map data, if it is a new map, WIKI may take 1 day or 2 to update...
![wiki_7_2](development.assets/wiki_7_2.jpg)
```
MAP.map_data = '''
ME ++ ME -- ME ME -- SP
MM ++ ++ MM -- -- ME --
ME -- ME MB ME -- ME MM
-- ME -- MM -- ME ++ ++
SP -- ME ME -- ME ++ ++
'''
```
The map information must contain at least the location of land and sea. There are still some difficulties in identifying land and sea
If the Spawn point (SP) is relatively remote, you must also include it
```
MAP.map_data = '''
-- ++ ++ -- -- -- -- SP
-- ++ -- -- -- -- -- --
-- -- -- -- -- -- -- --
-- -- -- -- -- -- ++ ++
SP -- -- -- -- -- ++ ++
'''
```
The meaning of these symbols is in `module/map/grid_info.py`.
| print_name | property_name | description |
|------------|----------------|-------------------------|
| ++ | is_land | fleet can't go to land |
| -- | is_sea | sea |
| __ | | submarine spawn point |
| SP | is_spawn_point | fleet may spawns here |
| ME | may_enemy | enemy may spawns here |
| MB | may_boss | boss may spawns here |
| MM | may_mystery | mystery may spawns here |
| MA | may_ammo | fleet can get ammo here |
| MS | may_siren | Siren/Elite enemy spawn |
6. **(Optional) Set sea weights**
The higher the value, the less you want the fleet to go. When not set, all are 10.
```
MAP.weight_data = '''
40 30 30 30 30 30 30 30
20 20 20 20 20 20 20 20
10 10 10 10 10 10 10 10
20 20 20 20 20 20 20 20
30 30 30 30 30 30 30 30
'''
```
7. **(Optional) Set the camera position*
```
MAP.camera_data = ['D3']
```
When not set, it will be generated according to the map size and camera field of view.
Manual setting can speed up the map scanning speed. For example, in 7-2, the automatically generated camera position is D2 D3 E2 E3. In fact, you only need to point the camera at D3 to see the entire map (A1 does not matter)..
8. **(Optional) Set enemy refresh information**
```
MAP.spawn_data = [
{'battle': 0, 'enemy': 3},
{'battle': 1, 'enemy': 2, 'mystery': 1},
{'battle': 2, 'enemy': 2, 'mystery': 1},
{'battle': 3, 'enemy': 1, 'mystery': 2},
{'battle': 4, 'enemy': 1},
{'battle': 5, 'boss': 1},
]
```
Here is a logic of what will be refreshed after each battle.This information is not on WIKI and needs to be collected manually.
In theory, it can be run without filling in. The function of the enemys refresh information is to correct the recognition errors and missing, and to capture the camera movement when the BOSS is refreshed.
9. **Expand the map**
```
A1, B1, C1, D1, E1, F1, G1, H1, \
A2, B2, C2, D2, E2, F2, G2, H2, \
A3, B3, C3, D3, E3, F3, G3, H3, \
A4, B4, C4, D4, E4, F4, G4, H4, \
A5, B5, C5, D5, E5, F5, G5, H5, \
= MAP.flatten()
```
This is to prepare for the subsequent writing of the enemy logic, which can be generated using the following code
```
shape = 'H5'
def location2node(location):
return chr(location[0] + 64 + 1) + str(location[1] + 1)
def node2location(node):
return ord(node[0]) % 32 - 1, int(node[1]) - 1
shape = node2location(shape.upper())
for y in range(shape[1]+1):
text = ', '.join([location2node((x, y)) for x in range(shape[0]+1)]) + ', \\'
print(text)
print(' = MAP.flatten()')
```
The py file can be found at `dev_tools/Flatten_generator.py`
10. **(Optional) Define map elements**
Such as defining roadblocks
```
ROAD_MAIN = RoadGrids([A3, [C3, B4, C5], [F1, G2, G3]])
```
### Set map parameters
The map parameters will override the default parameters and user parameters, with the highest priority.
If you don't know how to set it, just skip it. But it needs to be defined.
```
class Config:
pass
```
Refer to module.config.config for properties that can be set
For example, for the microlayer D3, because the map adds sea fog, different grid recognition parameters are required (the default parameters are for 7-2).
```
class Config:
INTERNAL_LINES_FIND_PEAKS_PARAMETERS = {
'height': (100, 220),
'width': 1,
'prominence': 10,
'distance': 35,
}
EDGE_LINES_FIND_PEAKS_PARAMETERS = {
'height': (255 - 80, 255),
'prominence': 2,
'distance': 50,
'wlen': 1000
}
```
There should be the following settings for enable siren recognition.
```
class Config:
MAP_HAS_AMBUSH = False
MAP_HAS_FLEET_STEP = True
MAP_HAS_MOVABLE_ENEMY = True
MAP_HAS_SIREN = True
MAP_HAS_DYNAMIC_RED_BORDER = True
MAP_SIREN_COUNT = 2
```
If only the least sea area information is entered, or there is no enemy refresh information, it should be noted that the map information is missing, and it will run in `wasteland mode` at this time
```
class Config:
POOR_MAP_DATA = True
```
For Crimson Echoes, the camera position was raised, the grid was reduced to 0.66.
```
class Config:
MAP_GRID_CENTER_TOLERANCE = 0.3
INTERNAL_LINES_HOUGHLINES_THRESHOLD = 50
EDGE_LINES_HOUGHLINES_THRESHOLD = 50
CAMERA_SWIPE_MULTIPLY_X = 200 * 0.7
CAMERA_SWIPE_MULTIPLY_Y = 140 * 0.7
COINCIDENT_POINT_ENCOURAGE_DISTANCE = 1.
MID_DIFF_RANGE_H = (45, 70)
MID_DIFF_RANGE_V = (97 - 3, 97 + 3)
TRUST_EDGE_LINES = True
VANISH_POINT_RANGE = ((540, 740), (-4000, -2000))
DISTANCE_POINT_X_RANGE = ((-2000, -1000),)
INTERNAL_LINES_FIND_PEAKS_PARAMETERS = {
'height': (80, 255 - 40),
'width': (0.9, 10),
'prominence': 10,
'distance': 35,
'wlen': 100,
}
EDGE_LINES_FIND_PEAKS_PARAMETERS = {
'height': (255 - 40, 255),
'prominence': 10,
'distance': 50,
'wlen': 1000
}
```
### Write search logic
- Run at default
```
class Campaign(CampaignBase):
MAP = MAP
```
- Simple logic
Write logic according to the first few battles, when missing, use the logic of the previous battle
Generally speaking, the logic of each battle should start with a question mark and clear roadblocks, then clear potential roadblocks, and finally return to the default battle logic
```
class Campaign(CampaignBase):
MAP = MAP
def battle_0(self):
self.clear_all_mystery(nearby=False)
if self.clear_roadblocks([ROAD_MAIN], strongest=True):
return True
if self.clear_potential_roadblocks([ROAD_MAIN], strongest=True):
return True
if self.clear_enemy(strongest=True, weight=True):
return True
return self.battle_default()
def battle_5(self):
self.clear_all_mystery(nearby=False)
if self.clear_roadblocks([ROAD_MAIN]):
return True
return self.fleet_2.brute_clear_boss()
```
To be continued
## How to support other server/language
### GUI
Copy `./module/config/argparser.py` to `argparser_xx.py` and change the argment.
Create a dictionary in `./module/config/dictionary.py` that translate your language to english.
Copy `alas_cn.py` to `alas_xx.py` and import `argparser_xx.py` . Then, edit server name.
> Format of .pyw file name: <sctipt_name>_<server_name>.pyw
>
> Script name is used to load ini file under `./config`, For example, alas_cn.pyw and alas_en.pyw both loads `./config/alas.ini`, but in different languages.
### Assets
Copy folder `./assets/cn` to `./assets/<your server>`, and replace the image. This will cost a lot of time to find, crop and test. Fortunately, if a image does not contain any charactors, it may works in all servers.
After replacing an image, don't forget to run `./dev_tools/button_extract.py`
### Class methods
Some method may be different in different servers. This decoractor is use to calls different function with a same name according to config (AzurLaneConfig instance).
```
from module.base.decorator import Config
from module.base.base import ModuleBase
class AnotherModule(ModuleBase):
@Config.when(SERVER='en')
def function(self):
# This method will be called only in EN server
pass
@Config.when(SERVER=None)
def function(self):
# This method will be called in other server
pass
```
### Other
There area also some modules difficult to change: the commission module.
In `./module/reward/commission.py`, I use [cnocr](https://github.com/breezedeus/cnocr) to recognize commission name in chinese, it may not works well in other languages.

View File

@ -1,165 +0,0 @@
# Write filter string in research
This doc will show how to write "filter string" to choose research projects.
本文档将阐述如何编写"过滤字符串"来选择科研项目.
## Select research series 选择科研期数
| Expression | Description | 描述 |
| ---------- | ----------------- | -------- |
| S1 | research series 1 | 一期科研 |
| S2 | research series 2 | 二期科研 |
| S3 | research series 3 | 三期科研 |
## Select project type 选择科研类型
| Expression | Description | 描述 |
| ---------- | ------------------------------ | ---------- |
| B | clear main chapter | 打主线图 |
| C | free research project | 白嫖科研 |
| D | face research | 定向科研 |
| E | disassemble gear | 装备分解 |
| G | coins research | 金币科研 |
| H | cube research | 魔方科研 |
| Q | plate research | 部件分解 |
| T | commission research | 委托科研 |
| DR | Azuma + Friedrich + Drake face | 彩科研定向 |
| PRY | other ships face | 其余定向 |
| Neptune | Neptune | 海王星 |
| Monarch | Monarch | 君主 |
| Ibuki | Ibuki | 伊吹 |
| Izumo | Izumo | 出云 |
| Roon | Roon | 罗恩 |
| SaintLouis | Saint Louis | 路易九世 |
| Seattle | Seattle | 西雅图 |
| Georgia | Georgia | 佐治亚 |
| Kitakaze | Kitakaze | 北风 |
| Azuma | Azuma | 吾妻 |
| Friedrich | Friedrich der Große | 腓特烈大帝 |
| Gascogne | Gascogne | 加斯科涅 |
| Champagne | Champagne | 香槟 |
| Cheshire | Cheshire | 柴郡 |
| Drake | Drake | 德雷克 |
| Mainz | Mainz | 美因茨 |
| Odin | Odin | 奥丁 |
## Select research duration 选择科研耗时
Use the duration in hours, such as `0.5`, `6`, `12`.
使用按小时计算的时间, 比如 `0.5`, `6`, `12`.
## Create filter string 创建过滤字符串
Link the selection with dash `-` , such as `S3-DR-0.5`, `S3-Q-0.5`, `Q-1`.
- Selections are all optional.
- You link without dash, such as `S3DR0.5` and `Q1`.
- You can use lowercase. It's not case sensitive.
Connect projects with greater than symbol `>` , such as
```
S3-DR-0.5 > Q-0.5 > S3-H-1 > S3-DR-2.5 > Q-1 > shortest
```
Alas will select research project from the left to the right. If no research matched, alas will do nothing. B (clear main chapter) and E (disassemble gear) will not be selected.
Some build-in strings:
- `shortest`, get the research project with the shortest duration. Equal to:
```
0.5 > 1 > 1.5 > 2 > 2.5 > 3 > 4 > 5 > 6 > 8 > 10 > 12
```
- `cheapest`, get the research project with the lowest cost. Equal to:
```
Q1 > Q2 > T3 > T4 > Q4 > C6 > T6 > C8 > C12 > G1.5 > D2.5 > G2.5 > D5 > Q0.5 > G4 > D8 > H1 > H2 > H0.5 > D0.5 > H4
```
- `reset`, example `x-xx > reset > x-xx`. Alas will refresh projects when selection reach there. If reset is already used today, do nothing.
使用横杠`-`连接选择, 比如 `S3-DR-0.5`, `S3-Q-0.5`, `Q-1`.
- 所有的选择都不是必须的
- 可以不用横杠连接, 比如 `S3DR0.5` and `Q1`.
- 可以用小写, 大写小写都无所谓.
用大于号`>`连接科研项目, 比如
```
S3-DR-0.5 > Q-0.5 > S3-H-1 > S3-DR-2.5 > Q-1 > shortest
```
Alas 会从左到右地选择科研项目. 如果没有科研符合要求, 什么都不做. B (打主线图) 和 E (装备分解) 不会被选中.
一些内置的字符串:
- `shortest`, 选择时间最短的科研项目. 相当于:
```
0.5 > 1 > 1.5 > 2 > 2.5 > 3 > 4 > 5 > 6 > 8 > 10 > 12
```
- `cheapest`, 选择消耗最少的科研项目. 相当于:
```
Q1 > Q2 > T3 > T4 > Q4 > C6 > T6 > C8 > C12 > G1.5 > D2.5 > G2.5 > D5 > Q0.5 > G4 > D8 > H1 > H2 > H0.5 > D0.5 > H4
```
- `reset`, 举例 `x-xx > reset > x-xx`. 当查找到此处时, Alas 会刷新科研列表. 如果今天的刷新已经用过了, 什么都不做.
## Use your filter string in Alas 在Alas中使用过滤字符串
Open config/alas.ini , search `research_filter_string`, and paste filter string there, like this:
Choose "research_filter_prefix" to be "customized" in Alas GUI, and press "run" to save settings.
打开 config/alas.ini , 找到 `research_filter_string`, 并在那粘贴过滤字符串, 像这样:
在 Alas GUI 中把 "科研项目选择预设" 设置为 "自定义", 并点击 "运行" 保存设置.
```
research_filter_string = S3-DR-0.5 > S3-0.5 > Q0.5 > cheapest
```
## Some pre-written filter string 一些预制的过滤字符串
- `series_3_fastest` Do series 3 ASAP, whatever it costs. 快速完成三期科研, 不惜一切代价.
```
S3-DR-0.5 > S3-0.5 > S3-DR-2.5 > S3-H-1 > S3-H-4 > S3-H-2 > S3-DR-8 > S3-DR-5 > Q1 > Q2 > reset > shortest
```
- `series_3` Do series 3, need gears, use cubes and coins.
```
S3-DR-0.5 > S3-0.5 > Q0.5 > S3-DR-2.5 > S3-DR-8 > S3-DR-5 > S3-H-1 > S3-H-4 > S3-H-2 > Q1 > Q2 > Q4 > reset > G1.5 > G2.5 > cheapest
```
- `series_3_than_2` Do series 3, also consider series 2, need gears, use cubes and coins. 做三期科研兼顾二期, 需要装备, 使用魔方和金币.
```
S3-DR-0.5 > S3-0.5 > S2-DR-0.5 > Q0.5 > S3-DR-2.5 > S3-DR-8 > S3-DR-5 > S3-H-1 > S3-H-4 > S3-H-2 > S2-DR-2.5 > S2-DR-8 > S2-DR-5 > Q1 > Q2 > Q4 > reset > G1.5 > G2.5 > cheapest
```
- `free_research_only` Do free research only. 只做白嫖科研.
```
C12 > C8 > C6 > Q1 > Q2 > Q4 > T3 > T4 > T6 > reset > cheapest
```
- `cubes_to_chips` Consume cubes in exchange of chips, need gears. 切魔方换心智单元, 需要装备.
```
Q0.5 > H0.5 > H1 > H2 > H4 > Q1 > Q2 > Q4 > reset > G1.5 > G2.5 > cheapest
```

View File

@ -1,172 +0,0 @@
# Future plan
Here list the future plan for Alas, sorted from easy to hard.
Alas has a lot of handful public functions and class methods, from object detection to map logics, don't handcraft them again.
## Plans in 2020.06
- ~~Test chapter 6 when using 1 fleet~~ Done
- ~~Add logic to chapter 2, 4, 5, when using 2 fleets~~ Done
> Chapter 1 to 5 are written in logic of 1 fleet, and I added the logic of 2 fleets in chapter 3, try to imitate that.
- Add more maps, from 7-3 to 13-4
> 9-2, 9-3, 9-4, 10-1, 10-2, already done. Thanks Cerzo, Lanser, whoamikyo
>
> Read doc/development.md for details.
>
> Some new logics may not included, read module/map/map.py
- ~~Add support for hard map~~ Already done
> Alas can use the map file from normal mode, no need to rewrite
>
> Add a new option in module/config/argparser.py, under the "main chapter".
>
> Add the arg name into module/config/dictionary.py
>
> Add arg into config/template.ini
>
> Load it in module/config/config.py
>
> Pass that parameter to `ensure_campaign_ui` in module/campaign/campaign_ui.py line 74
- Add support for dorm
> Add new file module/reward/dorm.py
>
> There should be options: DORM_REWARD_INTERVAL (1 hour, 2 hour, 3 hour, 4 hour), USE_GEM_FOOD (yes, no)
>
> Should able to pick up love and coin in dorm, may hard to pick all of them, not a big deal.
>
> Call OCR to detect food remain and total
>
> Don't use template matching to detect which food is available, because the naval curry change from time to time. The unavailable food items have a white overlay, make good use of that.
- Adaptive parameters for perspective detection
> When alas get an perspective error, it stops. Catch the errors, auto adjust the parameters and retry.
>
> Read perspective_en.md, see how alas do map detection, and you need to have some basic knowledge of perspective.
- Restart when get stuck or lost connection
> Don't simply use `handle_popup_confirm()`, because many module also use that.
>
> After the restart, retreat from map, because alas can't resume from a unfinished map.
>
> Alas have highly wrapped API, you may need to touch some low-level functions to do that.
>
> When you're done, test the speed, see if it effects on the performance.
- ~~Try ascreencap~~ Already done
> I notice that ALauto use ascreencap, test and see if it's faster than uiautomator2.
>
> If so, add that option in GUI, and set to default.
- ~~Update cnocr~~ 2020.07.15
> If we use the latest `cnocr`, we can recognize English letters better, get rid of `mxnet==1.4.1`, have smaller model size.
>
> But I already train 2 models under `cnocr==1.0.0`, named `stage` and `digit`, test if they are compatible.
- ~~Re-train ocr models~~ 2020.07.15
> The existing ocr model named `stage` and `digit` are poorly trained, you need to make sure letters are full-filled the image.
>
> The fat letters which is frequently used game is `Impact`
>
> The slim letters use in level and oil is `AgencyFB-Regular`
>
> The letters in research which looks cool is `MStiffHeiHK-UltraBold`
>
> It's better to use only one model to recognize all.
- Add support research
> Do this after updating cnocr and retraining models.
>
> Imitating commission.py, use ocr to get research name, classify them, then choose
- ~~Re-fit the relationship between map swipe and screen swipe~~ Done, the magic number is 1.626
> This function is `_map_swipe` in module/map/camera.py line 36. If you want to focus camera from D3 to E4, then it will do a swipe (-200, -140). A simple linear fit turn `(1, 1)` to be `(-200, -140)`.
>
> I fit this in 7-2, but i found it no working well in some large maps. Alas have to call `focus_to_grid_center` in line 89 to fix camera position. But sometimes that failed, alas update map data to the wrong grids, and cause a series of error until `ensure_edge_insight` is called.
>
> I do know that the relationship between map swipe and screen swipe is complex, but linear would be enough, because we won't swipe too far.
>
> How close is the camera on the map will also effect the relationship, you can use the `mid` of vertical lines the measure that. So closer camera means larger diff in vertical.mid, and further swipe, try to fit the the exact relationship.
- Combine perspective.py and homg_trans_beta brunch in ALauto
> asd111333 shared his thoughts on map detection to me. His project is doing so good when there is only few empty grids on screen which is the weakness of Alas, and ALauto don't have a global map view, which is the strengthen of Alas.
>
> perspective.py can generate the camera setting needed in his approach, so no longer need to hard-code camera settings for different maps.
>
> Alas work in 720p and ALauto works in 1080p, so there is a 1.5x scaling.
>
```
def get_homo_data(self, tile_width=209/1.5, tile_height=209/1.5):
hori = self.horizontal[0].add(self.horizontal[-1])
vert = self.vertical[0].add(self.vertical[-1])
src_pts = hori.cross(vert).points
x = len(self.vertical) - 1
y = len(self.horizontal) - 1
dst_pts = np.array([[0, 0], [x * tile_width, 0], [0, y * tile_height], [x * tile_width, y * tile_height]], dtype=float)
dst_pts += src_pts[0]
return [src_pts * 1.5, dst_pts * 1.5]
```
>I replaced the assets in 720p, now can directly generate the parameter for `cv2.warpPerspective`
```
def get_perspective(self):
tile_width = 140
tile_height = 140
# Generate homo from src_pts and dst_pts
perspective = Perspective(image, config=self.config)
hori = perspective.horizontal[0].add(perspective.horizontal[-1])
vert = perspective.vertical[0].add(perspective.vertical[-1])
src_pts = hori.cross(vert).points
x = len(perspective.vertical) - 1
y = len(perspective.horizontal) - 1
dst_pts = np.array([[0, 0], [x * tile_width, 0], [0, y * tile_height], [x * tile_width, y * tile_height]],
dtype=float)
dst_pts += src_pts[0]
homo, _ = cv2.findHomography(src_pts, dst_pts)
# Re-generate to align image
area = self.config.DETECTING_AREA
area = np.array([[area[0], area[1]], [area[2], area[1]], [area[0], area[3]], [area[2], area[3]]])
matrix = homo.dot(np.pad(area, ((0, 0), (0, 1)), mode='constant', constant_values=1).T)
x, y = matrix[0] / matrix[2], matrix[1] / matrix[2]
x, y = x - np.min(x), y - np.min(y)
homo, _ = cv2.findHomography(area, np.array([x, y]).T)
# Perspective transform
size = (np.ceil(np.max(x)).astype(int), np.ceil(np.max(y)).astype(int))
screen_trans = cv2.warpPerspective(np.array(perspective.image), homo, size)
screen_edge = cv2.Canny(screen_trans, 50, 100)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
screen_edge = cv2.morphologyEx(screen_edge, cv2.MORPH_CLOSE, kernel)
# Find an empty grid
res = cv2.matchTemplate(screen_edge, free_tile_center, cv2.TM_CCOEFF_NORMED)
_, similarity, _, location = cv2.minMaxLoc(res)
logger.info(f' free_tile_center_sim: {str(round(similarity, 3)).ljust(5, "0")}')
```
> I imagined a way to use two approach. After entering map, call perspective.py to generate camera data, and use homo_trans till the end. If get any error, fallback to perspective.
>
> This will be faster and with lower error rate, and get rid of the parameters of `scipy.signal.find_peaks`
>
> I got a problem now. Alas use the edge of the map to locate camera. If edge is not insight, locate camera from history swipe which may be unstable. When edges appear again, use that to correct camera location. So even if i use homo_trans, i still need to call perspective from time to time.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

View File

@ -1,147 +0,0 @@
# How to do statistics on item drop rate
> **DEPRECATED**
>
> This document uses cnocr==1.0.0, need old versions of Alas to run it.
This document will show how to use `dev_tools/item_statistics.py`
## Enable statistics in alas
In alas GUI,
- set `enable_drop_screenshot` to `yes`, if enabled, alas will add a 1s sleep before screenshot, to avoid capture the flash. There's a flash light when item shows up.
- set `drop_screenshot_folder` to be the folder you want to save. It is recommended to save it in SSD.
After few hours or few days of running, you will get a folder structure like:
```
<your_folder>
campaign_7_2
get_items
158323xxxxxxx.png
158323xxxxxxx.png
158323xxxxxxx.png
get_mission
get_ship
mystery
status
campaign_10_4_HARD
get_items
get_mission
get_ship
status
d3
get_items
get_mission
get_ship
status
```
Screenshot are named after millesecond timestamp.
## Prepare a new environment
- Prepare another virtual environment, accoring to `requirements.txt`. But use the GPU version of `mxnet`.
I am using GTX1080Ti, and I installed `mxnet-cu80==1.4.1`, `CUDA8.0`, `cuDNN`. Google `mxnet gpu install`, and see how to do in details. You may intall other version of CUDA, and mxnet for that CUDA, because you are using another graphic card.
- Look for the cnocr in your virtual environment. Replace site-packages\cnocr\cn_ocr.py line 89
```
mod = mx.mod.Module(symbol=sym, context=mx.cpu(), data_names=data_names, label_names=None)
```
to be:
```
mod = mx.mod.Module(symbol=sym, context=mx.gpu(), data_names=data_names, label_names=None)
```
Now cnocr will run on GPU.
You can skip these anyway, and use the same environment as alas, but the OCR will run really slow.
- Install tqdm, a package to show progressbar.
```
pip install tqdm
```
## Extract item_template
Copy folder `dev_tools\item_template` to the map folder such as `<your_folder>\campaign_7_2`.
Change the folder in line 24
These template are named in chinese, rename them in English.
>**How to a name template image**
>
>You should use their full name, such as "138.6mm单装炮Mle1929T3", instead of short name or nickname, such as "DD_gun".
>
>If you have same item with different image, use names like `torpedo_part.png`, `torpedo_part_2.png`, they will a classified as torpedo_part
Uncomment the part for item extract in dev_tools/item_statistics.py, and run, you will have some new item templates. Here's an example log:
```
1%| | 107/12668 [00:05<10:24, 20.10it/s]2020-06-03 10:39:42.609 | INFO | New item template: 50
1%| | 158/12668 [00:07<10:42, 19.47it/s]2020-06-03 10:39:45.098 | INFO | New item template: 51
2%|▏ | 207/12668 [00:10<10:33, 19.66it/s]2020-06-03 10:39:47.772 | INFO | New item template: 52
2%|▏ | 215/12668 [00:10<11:20, 18.29it/s]2020-06-03 10:39:48.304 | INFO | New item template: 53
100%|██████████| 12668/12668 [10:33<00:00, 19.99it/s]
```
Rename those new templates.
If you find some items haven't been extracted, try use line 140, instead of 141.
## Final statistic
Uncomment the part for final statistic, configure the csv file you wang to save.
The ocr model may not works fine in EN.
Here's an example log:
```
2020-06-03 12:23:55.355 | INFO | [ENEMY_GENRE 0.007s] 中型侦查舰队
2020-06-03 12:23:55.363 | INFO | [Amount_ocr 0.009s] [1, 1, 22]
100%|█████████▉| 14916/14919 [20:32<00:00, 13.20it/s]2020-06-03 12:23:55.442 | INFO | [ENEMY_GENRE 0.007s] 大型航空舰队
2020-06-03 12:23:55.455 | INFO | [Amount_ocr 0.013s] [1, 1, 1, 17]
2020-06-03 12:23:55.539 | INFO | [ENEMY_GENRE 0.007s] 敌方旗舰
2020-06-03 12:23:55.549 | INFO | [Amount_ocr 0.010s] [1, 2, 1, 63]
100%|█████████▉| 14918/14919 [20:33<00:00, 12.35it/s]2020-06-03 12:23:55.623 | INFO | [ENEMY_GENRE 0.007s] 精英舰队
2020-06-03 12:23:55.633 | INFO | [Amount_ocr 0.010s] [1, 1, 1, 17]
100%|██████████| 14919/14919 [20:33<00:00, 12.10it/s]
```
Now you got a csv file, formated to be:
```
<get_item_timestamp>, <battle_status_timestamp>, <enemy_genre>, <item_name>, <item_amount>
```
like this:
```
1590271317900,1590271315841,中型主力舰队,主炮部件T3,1
1590271317900,1590271315841,中型主力舰队,物资,23
1590271359374,1590271357251,小型侦查舰队,通用部件T1,1
1590271359374,1590271357251,小型侦查舰队,鱼雷部件T2,1
1590271359374,1590271357251,小型侦查舰队,物资,13
1590271415308,1590271413207,敌方旗舰,彗星,1
1590271415308,1590271413207,敌方旗舰,通用部件T3,1
1590271415308,1590271413207,敌方旗舰,科技箱T1,1
1590271415308,1590271413207,敌方旗舰,物资,42
1590271415308,1590271413207,敌方旗舰,_比萨研发物资,1
1590271415308,1590271413207,敌方旗舰,_鸢尾之印,1
```
You can open it in Excel or load it into database.
## Improvement
These code is running on single thread, you can try adding multiprocess to speed up. I didn't do that because it's still acceptable (20it/s without ocr, 12it/s with ocr)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 456 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 362 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 502 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 339 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 416 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 399 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 464 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 340 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

View File

@ -1,185 +0,0 @@
# 海图识别
`海图识别` 是一个碧蓝航线脚本的核心. 如果只是单纯地使用 `模板匹配 (Template matching)` 来进行索敌, 就不可避免地会出现 BOSS被小怪堵住 的情况. `AzurLaneAutoScript` 提供了一个更好的海图识别方法, 在 `module.map` 中, 你将可以得到完整的海域信息, 比如:
```
2020-03-10 22:09:03.830 | INFO | A B C D E F G H
2020-03-10 22:09:03.830 | INFO | 1 -- ++ 2E -- -- -- -- --
2020-03-10 22:09:03.830 | INFO | 2 -- ++ ++ MY -- -- 2E --
2020-03-10 22:09:03.830 | INFO | 3 == -- FL -- -- -- 2E MY
2020-03-10 22:09:03.830 | INFO | 4 -- == -- -- -- -- ++ ++
2020-03-10 22:09:03.830 | INFO | 5 -- -- -- 2E -- 2E ++ ++
```
module.map 主要由以下文件构成:
- perspective.py 透视解析
- grids.py 海域信息解析
- camera.py 镜头移动
- fleet.py 舰队移动
- map.py 索敌逻辑
## 一点透视
在理解 `AzurLaneAutoScript` 是如何进行海图识别之前, 需要快速了解一下 `一点透视` 的基本原理. 碧蓝航线的海图是一个一点透视的网格, 解析海图的透视, 需要计算出灭点和距点.
在一点透视中:
- 所有的水平线的透视仍为水平线.
- 所有的垂直线相交于一点, 称为 `灭点` . 灭点距离网格越远, 垂直线的透视越接近垂直.
![vanish_point](perspective.assets/vanish_point.png)
- 所有的对角线相交于一点, 称为 `距点` . 距点离灭点越远, 网格越扁长. 距点和灭点在同一水平线上. 距点其实有两个, 它们关于灭点对称, 图中画出的是位于灭点左边的距点.
![distant_point](perspective.assets/distant_point.png)
## 截图预处理
![preprocess](perspective.assets/preprocess.png)
拿到一张截图之后, `load_image` 函数会进行以下处理
- 裁切出用于可以用于识别的区域.
- 去色, 这里使用了 `Photoshop` 里的去色算法, (MAX(R, G, B) + MIN(R, G, B)) // 2
- 去UI, 这里使用 `overlay.png` .
- 反相
(上面的图是反相前的结果, 反相后的图过于恐怖, 就不放了)
## 网格识别
### 网格线识别
网格线, 是一条 20% 透明度的黑色线, 在 `720p` 下, 有3至4像素粗. 在旧UI时, 只需要把图像上下左右移动一个像素, 再除以原图像, 便可以得到网格线. 新UI的海图格子增加了白色框, 白色框有透明度渐变, 增加了识别难度.
`find_peaks` 函数使用了 `scipy.signal.find_peaks` 来寻找网格线. `scipy.signal.find_peaks` 可以寻找数据中的峰值点 : https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.find_peaks.html
截取 height == 370 处图像, 使用以下参数:
```
FIND_PEAKS_PARAMETERS = {
'height': (150, 255 - 40),
'width': 2,
'prominence': 10,
'distance': 35,
}
```
![find_peaks](perspective.assets/find_peaks.png)
可以看出, 有一些被遮挡的没有识别出来, 还有很多识别错误, 不过问题不大.
然后扫描每一行, 绘制出图像. (出于性能优化, 实际中会把图像展平至一维再识别, 这将缩短时间消耗至约 1/4.)
![peaks](perspective.assets/peaks.png)
至此, 我们得到了四幅图像, 分别对应 `垂直的网格内线` `水平的网格内线` `垂直的网格边线` `水平的网格边线` . 这一过程在 `I7-8700k` 上需要花费约 0.13 s, 整个海图识别流程将花费约 0.15 s.
注意, 识别内线和边线所使用的参数是不一样的. 不同的地图, 应该使用对应的参数, 如果偷懒的话, 也可以使用默认参数, 默认参数是针对 `7-2` 的, 可以在第七章中使用, 甚至可以用到 `北境序曲 D3` .
## 网格线拟合
`hough_lines` 函数使用了 `cv2.HoughLines` 来识别直线, 可以得到四组直线.
![hough_lines_1](perspective.assets/hough_lines_1.png)
`垂直的网格内线` 为例, 可以看到, 识别结果有一些歪的线.
我们在图片中间拉一条水平线, 称为 `MID_Y` (如果要修正水平线, 就拉垂直线), 交于垂直线, 交点称为 `mid` , 如果 `mid` 之间的距离小于3, 就认为这些线是相近线, 并用他们的平均值代表他们. 这样就修正了结果.
## 灭点拟合
我们知道, 在一点透视中所有垂直线相交于灭点, 但是网格识别的结果是有误差的, 不能直接求直线的交点.
`_vanish_point_value` 函数用于计算, 某一点到所有垂直线的距离, 并用 `scipy.optimize.brute` 暴力解出离直线组最近的点, 它就是 `灭点` . 这个曲面描绘了点到垂直线的距离和. 为了在求解是能大胆抛弃距离较远的线, 在求距离是加了 `log` 函数.
![vanish_point_distance](perspective.assets/vanish_point_distance.png)
得到灭点后, 还记得之前的 `mid` 吗, 将它们连接至灭点, 作为垂直线. 这是对结果的第二次修正.
## 距点拟合
将最初得到的垂直线和水平线相交, 得到交点. 我们知道距点和灭点在同一水平线上, 在这条水平线上取点, 将所有交点连接至这点, 得到斜线, `_distant_point_value` 函数将计算斜线的 `mid` 之间的距离, 同样使用 `scipy.optimize.brute` 暴力解出距离最小的点, 它就是 `距点` .
如果将斜线绘制出来, 会有这样的图像, 虽然有很多错误的斜线, 但确实求出了正确的距点.
![diatant_point_links](perspective.assets/diatant_point_links.png)
## 网格线清洗
经过以上步骤, 我们得到了以下网格线, 大体正确, 但是有错误.
![mid_cleanse_before](perspective.assets/mid_cleanse_before.png)
取垂直线的 `mid` ,
```
[ 185.63733413 315.65944444 441.62998244 446.89313842 573.6301653
686.40881027 701.20376316 830.27394123 959.00511191 1087.91874026
1220.58809477]
```
因为每个格子都是等宽的, 所以 `mid` 理论上是一个等差数列, 但实际识别结果可能有错误的项, 也可能有缺失的项. 我们用一次函数表达这个关系 `y = a * x + b`. 由于错误和缺失, 这里的 `x` 不一定是项数 `n` , 但只要没有10个以上的错误或者缺失, 就会有 `x ∈ [n - 10, n + 10]` .
接下来, 把表达式改写为 `b = -x * a + y` , 其中 `x ∈ [n - 10, n + 10]` . 如果把`a`当作自变量, 把`b`当作因变量, 那么这是一组直线, 它有 11 * 21 条. 把它们描绘出来:
![mid_cleanse_lines_with_circle](perspective.assets/mid_cleanse_lines_with_circle.png)
可以发现, 用橙色圈起来的地方有多条直线重合, 我们称为 `重合点` (`coincident_point`). 那些错误的 `mid` 产生的直线无法与其他直线交于重合点, 自然被剔除.
使用 `scipy.optimize.brute` 暴力求解所有直线的最近点, 得到`重合点` 的坐标
```
[-201.33197146 129.0958336]
```
因此一次函数就是 `y = 129.0958336 * x - 201.33197146` .
> 在计算点到直线的距离时, 使用了以下函数:
>
> ```
> distance = 1 / (1 + np.exp(9 / distance) / distance)
> ```
> 这个函数将削弱距离较远的直线的影响, 鼓励优化器选择局部最优解.
>
> ![mid_cleanse_function](perspective.assets/mid_cleanse_function.png)
>如何处理水平线?
>
>过`距点`作任意一条直线, 与水平线相交. 将得到的交点与`灭点`连接, 就完成了水平线到垂直线的映射. 处理完再映射回水平线即可.
>
>![mid_cleanse_convert](perspective.assets/mid_cleanse_convert.png)
最后, 以海图或者屏幕为边界生成 `mid` , 此时缺失的 `mid` 也得到了填充. 重新连接至灭点, 完成了垂直线的清洗.
绘制出网格识别的结果:
![mid_cleanse_after](perspective.assets/mid_cleanse_after-1584008112022.png)
# 网格裁切
事实上, 海域中的舰娘, 敌人, 问号等, 都是固定在网格中心的图片, 只不过这些图片会因为透视产生缩放而已. 注意, 仅仅是缩放, 图片不会因为透视产生变形, 产生变形的只有地面的红框和黄框.
![crop_basic](perspective.assets/crop_basic.png)
`grid_predictor.py` 中提供了 `get_relative_image` 函数, 它可以根据透视, 裁切出关于网格中心相对位置的图片, 统一缩放到特定大小, 这样就可以愉快地使用模板匹配了.
```
from PIL import Image
from module.config.config import cfg
i = Image.open(file)
grids = Grids(i, cfg)
out = Image.new('RGB', tuple((grids.shape + 1) * 105 - 5))
for loca, grid in grids.grids.items():
image = grid.get_relative_image(
(-0.415 - 0.7, -0.62 - 0.7, -0.415, -0.62), output_shape=(100, 100))
out.paste(image, tuple(np.array(loca) * 105))
out
```
![crop_scale](perspective.assets/crop_scale.png)
## 海域信息解析
未完待续

View File

@ -1,188 +0,0 @@
# Map Detection
Map detection is the core of an Azur lane bot. If simply using `template matching` to do the enemy detection, it will inevitably appear BOSS block by enemies. AzurLaneAutoScript (Alas), provides a better approach of map detection. In module.map, you can get full information in map, such as:
```
2020-03-10 22:09:03.830 | INFO | A B C D E F G H
2020-03-10 22:09:03.830 | INFO | 1 -- ++ 2E -- -- -- -- --
2020-03-10 22:09:03.830 | INFO | 2 -- ++ ++ MY -- -- 2E --
2020-03-10 22:09:03.830 | INFO | 3 == -- FL -- -- -- 2E MY
2020-03-10 22:09:03.830 | INFO | 4 -- == -- -- -- -- ++ ++
2020-03-10 22:09:03.830 | INFO | 5 -- -- -- 2E -- 2E ++ ++
```
module.map mainly consists of the following files:
- perspective.py Perspective detection
- grids.py Grid data parsing
- camera.py Camera moving
- fleet.py Fleet moving
- map.py Map logics for enemy searching
## One Point Perspective
Before understanding how alas do map detection, we have to go through some basic knowledge of `one point perspective`. Map of Azur Lane is grid in one point perspective. Parsing perspective needs to calculate `vanish point` and `distant point`.
In one point perspective:
- the perspective of horizontal lines are still horizontal lines.
- the perspective of all vertical lines intersect at one point, called `vanish point`. The further a vanish point away from grids, the perspective of vertical lines closer to 90 degree.
![vanish_point](perspective.assets/vanish_point.png)
- All diagonals of the grids intersect at one point, called `distant point`, The further a distant point away from grids, the grid become fatter. In fact, there are 2 distant point, the following image draws the one to the left of vanish point.
![distant_point](perspective.assets/distant_point.png)
## Screenshot Pre-processing
![preprocess](perspective.assets/preprocess.png)
When perspective.py gets an screenshot, function `load_image` do such process:
- crop area of detection
- to grayscale, Using the algorithm in Photoshop, (MAX(R, G, B) + MIN(R, G, B)) // 2
- cover UI. Here use `overlay.png`
- Reverse color
(Image above is before reverse, because the reversed image is too terrified to show)
## Grid Detection
### Detecting Grid Lines
Grid lines are black lines with a transparency of 20%. In 720P, it has 3 to 4 pixel wide. During the period of "old-UI", we simply move the image 1px and divide by the origin image to detect grid lines. White frame with transparency gradient is added in "new-UI", which increase the difficulty of detection.
Function `find_peaks` use `scipy.signal.find_peaks` to find grid lines. `scipy.signal.find_peaks` can find peaks of given data.
Crop image at height == 370, use following parameters:
```
FIND_PEAKS_PARAMETERS = {
'height': (150, 255 - 40),
'width': 2,
'prominence': 10,
'distance': 35,
}
```
![find_peaks](perspective.assets/find_peaks.png)
As you can see, some grid lines are not detected and has many mistake as well. Not a big deal.
Scan every row and draw the image. (For better performance, image will be flatten to 1-D array before detection, which will reduce time cost to 1/4.)
![peaks](perspective.assets/peaks.png)
We gets 4 images so far, they are `vertical inner lines`, `horizontal inner lines`, `vertical edge lines`, `horizontal edge lines`. This process takes about 0.13 s on `I7-8700k` , and the full map detection process will take about 0.15 s.
P. S. Parameters use to detect inner lines are different from edge lines. In different maps, we should use different parameters. If you are lazy, you can use the default parameters, which is for 7-2. Those parameters can be used in Chapter 7, can even be used in `北境序曲(event_20200227_cn) D3`.
## Fitting Grid Lines
Function `hough_lines` use `cv2.HoughLines` to detect lines. Now we have 4 group of lines.
![hough_lines_1](perspective.assets/hough_lines_1.png)
Take `vertical inner lines` for example. There some incorrect lines.
We create a horizontal line at the middle of image, called `MID_Y`, (When fixing vertical lines, create a vertical one), and cross `vertical inner lines`, those crossing points are called `mid`. If the distance between two mids smaller than 3, we treat them as a group of lines, and replace them with their average. After that, we corrected the result.
## Fitting Vanish Point
As mention above, all vertical lines in one point perspective intersect at one point. There are errors in vertical lines, so we can't solve the equations to get that.
Function `_vanish_point_value` , use to calculate the distance between a point and a group of lines, and use `scipy.optimize.brute` to brute-force solve the closest point to vertical lines, which is called `vanish point`. This surface shows the sum of distance from the point to the group of vertical line. In order to ignore wrong lines far away from vanish point, it uses logarithm.
![vanish_point_distance](perspective.assets/vanish_point_distance.png)
Still remember `mid` ? we re-link then to vanish point, and act as vertical lines. This is the 2nd correction.
## Fitting Distant Point
We intersect the corrected vertical lines and the origin horizontal lines. `distant point` and `canish point` are on the same horizontal line, so we take a point on this horizontal line, and link all intersection, get `oblique lines`. Function `_distant_point_value` calculates the distance between the `mid` of oblique lines. Also use `scipy.optimize.brute` to brute-force solve the closet point, called `distant point`.
This image draws the oblique lines. Although there are many mistakes, it do gets the correct point.
![diatant_point_links](perspective.assets/diatant_point_links.png)
## Cleansing Grid Lines
With the above process, we get grid lines like this. It's generally correct, but with mistakes.
![mid_cleanse_before](perspective.assets/mid_cleanse_before.png)
Take the `mid` of vertical lines.
```
[ 185.63733413 315.65944444 441.62998244 446.89313842 573.6301653
686.40881027 701.20376316 830.27394123 959.00511191 1087.91874026
1220.58809477]
```
We know all grid has a same width, so theoretically, `mid` is an arithmetic progression, but with wrong members and missing members. Use a linear function `y = a * x + b` to describe that. Because of mistakes and missing, the `x` in linear function may not be the number `n` in arithmetic progression. As long as mistakes less than 10, there will have `x ∈ [n - 10, n + 10]` .
Then, transform the linear function as `b = -x * a + y`, and `x ∈ [n - 10, n + 10]` . If treat `a` to be independent variable and treat `b` to be dependent variable, it's a group of lines with amount of 11 * 21. Draw them.
![mid_cleanse_lines_with_circle](perspective.assets/mid_cleanse_lines_with_circle.png)
Discover that many lines intersect at where the orange circle pointed out, we call them `coincident point`. Those incorrect `mid` from incorrect lines can't intersect there, and get deleted.
Use `scipy.optimize.brute` to brute-force solve the coordinate of the best `coincident point`.
```
[-201.33197146 129.0958336]
```
So the linear function is `y = 129.0958336 * x - 201.33197146` .
> When calculating distance to the lines, it uses this function
>
> ```
> distance = 1 / (1 + np.exp(9 / distance) / distance)
> ```
> This function makes it less effect by lines far away, encourage optimizer to choose the local minimum.
>
> ![mid_cleanse_function](perspective.assets/mid_cleanse_function.png)
>How to cleanse horizontal lines?
>
>Make any line through `distant point`, link intersections and `vanish point` . This finish a map relation from horizontal lines to vertical lines. When cleanse finished, do a reversed process.
>
>![mid_cleanse_convert](perspective.assets/mid_cleanse_convert.png)
At last, generate `mid`, and crop it with the edge of map and screen. Missing `mid` get filled now. Re-link `mid` to vanish point, and the cleansing of grid lines is finished.
Draw results:
![mid_cleanse_after](perspective.assets/mid_cleanse_after-1584008112022.png)
# Grid Cropping
In fact, shipgrils, enemies, mystery are images fixed on grid center. They are scaled because of perspective.
P. S. They are scaled only, but not perspective transform. Only red border and yellow border on the ground are perspective transformed.
![crop_basic](perspective.assets/crop_basic.png)
In `grid_predictor.py`, provides function `get_relative_image` , which do crops according to grid center, and rescale to given shape. Now we can simply use template matching.
```
from PIL import Image
from module.config.config import cfg
i = Image.open(file)
grids = Grids(i, cfg)
out = Image.new('RGB', tuple((grids.shape + 1) * 105 - 5))
for loca, grid in grids.grids.items():
image = grid.get_relative_image(
(-0.415 - 0.7, -0.62 - 0.7, -0.415, -0.62), output_shape=(100, 100))
out.paste(image, tuple(np.array(loca) * 105))
out
```
![crop_scale](perspective.assets/crop_scale.png)
## Parsing Grid Data
To be continued.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB