有关使用OpenCV和机器学习自动化在线麻将(Mahjong Soul)的故事


总览

通过使用python中的OpenCV模板匹配和GUI操作模块,
BOT可以识别并单击Web浏览器上的麻将牌,并且播放可以自动进行。
另外,机器学习用于单击麻将牌的逻辑部分。

如果替换模板匹配搜索图像,则不仅可以在麻将灵魂中使用它,而且还可以在一般的其他麻将游戏中使用它,并且如果更改机器学习部分,将反复要求您根据具体情况做出合理的选择。条件,它可以应用于所有游戏。

*整个文章中都使用麻将灵魂的游戏内图像来帮助理解其内容,但从版权保护的angular来看,它们却非常模糊。

目标观众

(我喜欢麻将)想要接触机器学习的人
有兴趣使Windows和GUI操作自动化的人,但想知道什么是OpenCV

一个喜欢麻将灵魂的人,但是因为跑得太困难而很难进行试玩
对过去在大学图像处理实验室中的消磨时间脚本感兴趣的人

我的不纯动机

前几天,我爱上了麻将之魂。最近我很累。

如果您每天玩几场游戏或参加某个活动,您可以获得gacha奖励,所以继续玩游戏并接一个可爱的女孩我想

但是我再也不能和一个甚至知道规则的陌生人相处了这样的麻将。
(特别是该事件与其他玩家匹配,无论其能力如何,并且由于打出的排名和奖励不匹配,因此很难认真进行比赛)

但是,如果仅移动简单的Tsumokiri BOT,将被判断为不在办公桌旁,并且最差的帐户将被禁止。 (对此我感到很惊讶。不要这样做。)

通过说

  • 通过模拟鼠标单击来操作浏览器
  • 您可以选择瓷砖并扔掉并进行攻击

我决定进行BOT。

零件

收购手瓷砖和瓷砖

概述

我使用了一种称为OpenCV模板匹配的机制。

OpenCV(开源计算机视觉库)...一种由英特尔开发和发布的开源计算机视觉库。一个梦幻般的库,可以实现在计算机上处??理图像和视频所需的各种功能。

模板匹配...类似于梦境的过程,在输入图像中搜索与模板图像(部分图像)最相似的部分。

我本可以用C#将其制作为Windows应用程序,但是直接向麻将灵魂收费要比花大量精力更快。这次,我采用了python,它可以快速访问OpenCV做到了。

安装python

之后

1
$ pip install opencv-python

您可以仅通过

使用OpenCV。我以前很难做教程...
我认为您应该参考这里进行环境建设

使用pip安装OpenCV
https://qiita.com/fiftystorm36/items/1a285b5fbf99f8ac82eb

预先准备所有图案图块图像,从实际的播放屏幕截图中切出13个??图块,并搜索匹配的图块图像以获取图块信息。

屏幕截图注释

请注意,

浏览器大小=应用程序屏幕大小不成立。

Mahjong Soul是一个Web应用程序,旨在匹配任何分辨率的纵横比,如通过转换浏览器可以看到的。 1
换句话说,由浏览器变形且与游戏的原始纵横比不匹配的绘图区域显示为深黑色区域。

这里,模板匹配容易受到尺寸扩大或缩小的检测目标的影响,因此在过程开始时获取屏幕(捕获)尺寸后,插入调整过程以匹配准备好的切片模板的尺寸形象很重要。
(如果每次使用固定的显示环境浏览器大小玩游戏,则此过程是不必要的,因为捕获的图像和模板图像的大小每次都匹配。)

*还分发了用于捕获浏览器的各种python模块,但是我的方法很简单,只需进行全屏捕获,然后通过模板匹配检测应用程序屏幕边缘周围的位置。应用程序屏幕。

image.png
预先将尽可能多位于两点的图像元素提取为模板图像,以便在计算比例时误差较小。
一个好主意是选择一个不会随情况而变化并且在屏幕上没有相似功能(易于识别的≈特征)的图像。

这时,准备3-5种不同大小的模板图像(或在处理过程中更改比例),并采用匹配度和比例最高的模板图像。 2(由于准备为模板的图像的尺寸与实际拍摄的图像不同,并且如果屏幕区域检测失败,整个过程将变得不稳定)

通过预先测量提取为模板图像的图像的大小和当时的两点之间的距离并将其保存为数据,可以获取手瓷砖衬里的位置的相对位置即使显示环境发生变化,您也可以做到。
通过基于任一模板一张一张地切开手瓷砖(cv2.crop),可以如下所示提取图像。

(有些棘手,可能是因为手动收集了作为模板来源的手绘图像数据。这没关系,因为它不会显着影响识别精度。)

image.png

多拉(Dora)牌始终具有闪亮的屏幕效果,但是我对图像处理有不好的感觉(笑)

Tehai认可

现在,为了识别图块本身,模板匹配从"图块列表图像"中提取的每个图块图像,如下所示。

image.png
(*输入的Kuman图像与图块列表图像的一部分作为模板匹配)

创建数据,其中图块在水平行中排列,并且检测到的图像的x坐标将告诉您图块的真实身份。
(很难使所有这些麻将牌并排显示数据...)
image.png
该过程似乎很好,但是到目前为止花费了2.7秒。 .. ..
麻将灵魂有5秒,因此每个过程最多可以花费5秒。
再说一遍,不能说循环处理的开始=碰到的时刻,因此监视循环至少每2.5秒一次(回合开始→处理开始(经过最大2.5秒后)→完成处理(2.5s)→必须完成转弯),因此感觉很尴尬(;'?')机器学习部分也需要处理时间...好吧,让我们稍后对其进行重构! 3

"切什么"问题

概述和先例

13我搜索了一个程序,该程序通过输入手部瓷砖和瓷砖输出应该扔掉的瓷砖。没有。
实际上,麻将游戏AI领域正在争夺这一领域。对不起年长者。

此外,我的目的不是"我想到的最强大的Majan AI",而是一个持续运行如此潮湿以至于不能确定麻将灵魂管理为机器人的BOT,因此我将适当地设置此区域。

我试图用关键字" mahjong"将其分成著名的亚马逊github,

在HTML5 JavaScript上运行的麻将应用程序"网络麻将"中的"削减内容"判断部分
https://github.com/kobalab/Majiang
[操作演示] http://kobalab.net/majiang/dapai.html

这看起来不错。
作者在他的博客上留下了与"削减"判断逻辑有关的斗争日志,因此,我认为这对于那些追求最强AI的人会很有帮助。

但是,由于这是JavaScript,因此连接python的数据输入/输出有点麻烦。 .. ..
我一直在寻找可以在python中使用的东西。

采用机器学习

通过深度学习削减麻将的AI
https://github.com/hogeki/dlmahjong

对于那些从未接触过机器学习的人来说,自述文件很难相处,因此下面介绍了git clone运行后的过程。

首先,我们将引入带有pip的TensorFlow 4,但在这里,如果版本完全匹配,将会更快乐。认真地。您应该听听人类支柱的故事(真面目)

1
pip install tensorflow==1.7.0 (2020.8.5現在の当該gitレポジトリの内容に従う)

幸运的是,还包括Tenhou的得分数据(.txt),因此您可以按原样学习并将其保存为参数。

1
python mahjong_ai.py --train --save

现在应该将训练结果参数另存为外部数据(训练模型/训练模型)。
将来,通过使用该模型作为参数,可以执行判断处理以减少多少。

相应的git脚本没有实现"将13 1的切片数据作为信息传递并告诉您要剪切的内容"的功能(仅连续测试及其性能评估将在后面说明)。)您需要自己制作该部分。代码如下。

如果在Tenhou规范的字符串中输入手部瓷砖的信息作为参数,它将告诉您要切割的内容。 (以上训练模型为默认参数)

实施脚本

mahjong_ai.py(附加部分)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#calc_dahai関数は外部ファイルから呼び出すことを想定しているので
#付随するファイルをmahjong_ai.pyに付随するファイルをimportで参照
import mahjong_common as mjc
import mahjong_loader as mjl

#機能追加に伴うimportの追加
import unicodedata
import re

def init_sess():

    #calc_dahai関数は外部ファイルから呼び出すことを想定しているので
    #sessを設定する部分を関数化し、globalで保存させる。
    # (sessが何かということについてはmahjong_common.pyから読めます)
    global sess

    make_model()
    saver = tf.train.Saver()
    sess = tf.Session()
    sess.run(tf.global_variables_initializer())
    sess.run(tf.local_variables_initializer())
    saver.restore(sess, "ckpt/my_model")

def calc_dahai(hands_str,tsumo_str):

    #第1引数 入力例
    #hands_str="6p6p7p8p3s4s5s5s7s8s東東東"

    #第2引数 入力例
    #tsumo_str="6s"

    #実行前にinit_sess()しないと動かない
    global sess

    #手牌文字列を手牌1つごとの文字列の配列に変換
    tehai_tmp=[]

    #1バイト文字と2バイト文字が混在していて処理しづらいので、2バイト文字→1バイト文字に変換
    #(クソのような仕様だが、天鳳からの入力データがそうなっているのだからしょうがない)
    hands_str = hands_str.replace('東', 'HI')
    hands_str = hands_str.replace('南', 'MI')
    hands_str = hands_str.replace('西', 'NI')
    hands_str = hands_str.replace('北', 'KI')
    hands_str = hands_str.replace('白', 'SI')
    hands_str = hands_str.replace('発', 'HA')
    hands_str = hands_str.replace('中', 'NA')

    #2文字ごとに配列に格納
    tehai_str_list_by_2char = re.split('(..)',hands_str)[1::2]

    #文字を元に戻す
    tehai_str_list_by_2char = [s.replace('HI', '東') for s in tehai_str_list_by_2char]
    tehai_str_list_by_2char = [s.replace('MI', '南') for s in tehai_str_list_by_2char]
    tehai_str_list_by_2char = [s.replace('NI', '西') for s in tehai_str_list_by_2char]
    tehai_str_list_by_2char = [s.replace('KI', '北') for s in tehai_str_list_by_2char]
    tehai_str_list_by_2char = [s.replace('SI', '白') for s in tehai_str_list_by_2char]
    tehai_str_list_by_2char = [s.replace('HA', '発') for s in tehai_str_list_by_2char]
    tehai_str_list_by_2char = [s.replace('NA', '中') for s in tehai_str_list_by_2char]

    #手牌文字列の配列をmahjong_common.pyで決められた数値の配列に変換
    for tehai_one in tehai_str_list_by_2char:#tehai_tmp:
        h = mjc.get_hai_number(tehai_one)
        tehai_num_array[h] += 1

    print("--------麻雀AIの判定結果--------")

    tstr = mjc.get_string_from_tehai(tehai_num_array)
    print("手牌:" + tstr)

    tsumo = mjc.get_hai_number(tsumo_str)
    print("自摸:" + mjc.get_hai_string(tsumo))

    tehai_num_array[tsumo] += 1

    #ツモしたかどうかを判定する関数も用意されているので
    #成績を収集したり個別になんらかの処理をさせることも可能
    #if mjc.is_agari(tehai_num_array):
    #    print("和了")

    #機械学習による判定処理
    ai_outs = sess.run(out, feed_dict={x:[tehai_num_array]})

    dahai = get_ai_dahai(tehai_num_array, ai_outs[0])
    #print("打:" + mjc.get_hai_string(dahai))

    return(mjc.get_hai_string(dahai))

另外,它不是本文的主要目的,但作为此git存储库的原始功能,使用学习创建的模型来循环随机麻将牌模拟器就足够了。可以通过随机次数的测试来测试蛋彩画速率等。

1
python mahjong_ai.py --run

image.png
[模拟1000次的结果]

如果您想改善AI的功能,请收集更多的分数数据以进行学习,或者培训专业人员和好人的分数数据。
有趣的是,更改训练数据会更改测试结果。

在这里我将不对机器学习进行描述,但是我已经请您简要介绍了机器学习的基础知识,因此请尝试一下。 如果您的AI不错,请给我
顺便说一下,此时收集大量Tenhou的得分数据似乎有点困难。
如果您有一个可以收集数据的好脚本,请在注释中分享它,它将对全人类尤其是我有所帮助。

鼠标模拟

我尝试了各种方法,但是下面的教程很容易理解。
用管道输送所需的模块后,将可以复制和粘贴。

让Python使用PyAutoGui进行迭代工作
https://qiita.com/hirohiro77/items/78e26a59c2e45a0fe4e3

计算器应用程序将在您面前启动,并且在未经允许的情况下拖动鼠标。
乍一看,我什至为"哦"鼓掌。

如果仅复制和粘贴单击鼠标所需的部分,然后单击通过上述操作计算出的坐标位置,则可以。

揉和混合三个元素,直至完成。
代码描述不再省略。如果有需求,我将其提高到git。

运行状态(视频)

じゃんたまプレイ動画
点击缩略图以跳转到youtube

的链接

这是一个中等气味的游戏,但是看起来您可以选择朝Agari的方向盘。

Mechanko机器的优势在于可以同时处理简单的图块选择和对人来说有些混乱的图块选择。

观察,当前问题如下。

(1)我设法做出18招,以至于我无法用完20秒钟的时间,但根据情况,我可能会超过我的时间。

这部分有重构的空间,但是我不在乎,因为超时可以丢失。

毕竟,我不希望弹出或弹出,所以如果您在玩游戏时始终选择"不发出尖叫"选项,则处理似乎会更加稳定。

(2)由于学习时的数据不包括有关另一个家庭的废弃瓷砖和多拉的信息,因此即使另一个家庭达到了视频中的要求,瓷砖也不会晃动,并且dora瓷砖不会打扰你,扔掉(笑)

如果您对机器学习感兴趣并且想要增强此功能,则下一步是学习Dora信息和自发/冷信息。
扩展将不同维度的数据(即数据的含义)作为单个输入参数进行训练的能力是重要的一步。 (Mahjong_common.py端需要修复)

(但是,如果学习中包括河流信息和其他家庭的到达信息,则输入参数将变得巨大,如果设计不当,则稳定操作本身将很困难)。

③不支持Damaten,Tenpai和Dark Can。

这是一台从概念本身开始就针对Tsumoagari的机器。

之后

禁令已完成(笑)

是个玩笑,所以我重构了模板匹配的次数和频率以及处理过程,以免用完时间,并在联赛中也使用了它。

您可以赢,也可以输,也可以输,但是毕竟机器并不会累,所以它一直在播放。 (当前是渐进式的,仅在计算机运行时...)

我想评估一下模型的能力,该模型简单地训练了开始时引入的git中包含的Tenhou数据,以最终稳定在哪个联赛中。

我希望以后再添加一次,因此请期待。

◆关于在文章中担心会侵犯麻将灵魂的权利

这次,我在服务中发布了一个麻将视频的视频,在BOT的文章中,我不打算管理麻将灵魂,但在不违反官方准则的范围内使用了该视频,并且我们已经确定我们没有侵犯我们的权利。
但是,如果您参考本文进行相同的操作,请自担风险。
作为参考,我们将介绍可能与以下内容冲突的麻将灵魂的条款和条件。

实况游戏分发和视频发布指南(摘录自违禁物品)

?用于实时展示游戏以外的目的,例如政治,宗教,宣扬特定信念(适用w)等。

麻将灵魂使用条款第11条禁令

(12)通过该服务非法获取公司提供的各种内容的行为,或促销该行为的行为。 (通过BOT操作等获得gacha奖励)
(16)干扰或可能干扰此服务的操作或使用的行为。
(21)符合前述项目的行为以及公司认为不适当的其他行为。

  • 后来的验证表明,宽高比可能略有波动,但似乎在误差范围内。在我的脚本中,我决定分别测量和校正垂直和水平比例尺。 ?

  • 我认为关于此内容有各种各样的理论,但这是我的最佳实践。当您采用一种称为模板匹配的技术时,这个问题总是会发生,但是您知道其他任何好的方法(用更少的时间)吗? ?

  • 到目前为止,我还没有尝试对到目前为止已经进入的项目进行重构。主要是由于缺乏资源或动力。 .. .. ?

  • TensorFlow ...由Google开发并作为开放源代码发布的软件库,供机器学习使用。 ?