克隆人的攻击:现代机器人驱动技术

“现在,您编写了生活中最困难的程序,它将简单地将两个数字相加。”
Irina Ryzhova


智能赌博在线游戏是机器人的一个小窍门即使游戏软件的开发人员花了很多钱来抓捕机器人,例如在在线扑克室中,也仍然很可能绊倒“智能机器人”,与之进行一对一的游戏。特别是如果该漫游器绝对是无敌的……因为没有钱可以保护大量资金通过的系统。


1. Botovody来


隐藏的威胁


智力赌博在线游戏是不安全的。那些真正的金钱在旋转的人尤其不安全。他们的不安全感主要表现在这样的事实:无法确定您正在与谁玩耍:与一个在世的人或与一个机器人。在第二种情况下,游戏将进入一个目标。在具有“兴趣”的游戏中,例如mail.ru上的国际象棋和棋盘游戏,-在线游戏的所有者通过手指查看botovotirovanie。在多人在线游戏中,某些程度上具有实况转账功能,因此更多地关注捕捉机器人。但是,即使抑制僵尸程序的方法极为严格(例如在在线扑克室中所做的工作),仍然有很高的可能性绊倒成僵尸程序。

扑克软件的开发人员花费大量金钱来暴露机器人,但是,在线扑克botswana仍在蓬勃发展。没有钱可以保护大量金钱通过的系统。 Botovodov和机器人拦截器-分析对手的把戏并采取适当的对策-交替获胜。有人可能认为他们之间的战斗永远不会结束。但是,有一种安全的机器人驾驶方案,在此之前,机器人捕捉器可以通过。有趣的是,即使“争取正义的战士”将详细掌握了该机器人的原始代码,他们也将无法确定其使用的事实。该方案的实施是昂贵的事件,但是,由于潜在的好处是巨大的,所以该方案是非常相关的。

#

– , . – , . ( , ), «-» ( , - ). , , , , -, . , . , , , - ( ); , .. - . - , . - (3, , , , ..). . . . – - -- ( , ). -, , , , , – . - – - , - ( . « »).




: , .首先,机器人通过截屏或拦截网络流量来拍摄游戏当前状态的图片。其次,僵尸程序会决定在这种情况下的处理方式,有时会诉诸第三方软件(例如,对于国际象棋,它可以与某种国际象棋引擎进行交互)。最后,第三,该机器人模仿用户与键盘和鼠标的交互。此外,机器人捕获程序试图跟踪和破坏的三元循环会重复进行。因此,博托沃多夫与“正义战士”之间的战斗发生在三个方面。这场战斗有着悠久而激动人心的历史,但我们将仅着眼于最终决斗,在这场决斗中,“正义战士”在绝对无敌的僵尸面前将遭受无条件的惨败。

通过无形的摄影,无形的分析和无形的仿真来实现无敌的机器人技术。无法在一台计算机上执行此操作。首先,因为现代扑克软件具有rootkit的元素。

#

. , , , , - : « , ». - « » « » . , , – .

, . , . , – , , -. – .

, , , : «» , . , – , – , «- ».

– . , « », « » . , – , – . , , – .

, , . , , . , , , , - , . – «».


, .. , .解决方案是在不是玩游戏的工作站上,而是在单独的计算机(模拟器)上启动bot。然后,在三种偏执的条件下,扑克软件将没有机会认识到仿真的事实:1)工作站和仿真器不能通过网络进行通信; 2)为了拍摄游戏的当前状态,您应该使用视频卡的模拟输出,-使用视频捕获卡连接到“筛选器计算机”; 3)要模拟键盘和鼠标,应使用硬件-软件小工具,其输入连接到仿真器计算机,而输出-连接到两个PS / 2工作站,从中进行游戏。扑克软件不知道模拟视频输出和PS / 2是计算机上已连接了其他设备。



模拟键盘和鼠标时,应考虑相关的生物特征和技术特征情况,也可以通过扑克软件进行监视。至于生物识别,在模拟键盘和鼠标时,应注意模拟与真实情况相似的身体动作。应该记住的是,首先,一个有生命的人不能同时在16张桌子上同时长时间稳定地节奏演奏,特别是在8英寸显示器上。其次,一个有生命的人不能每周6天每天玩28个小时。最后,第三,一个活着的人的动作可以改变-取决于他在计算机上花费的时间。要模拟可能的生物识别情况,您将需要:1)“ PS / 2嗅探器”-软件和硬件乳液,它将侦听键盘和鼠标发送到PS / 2端口的数据流,并将听到的所有内容保存在单独的文件中,随后将用于模拟可能的生物特征识别环境; 2)只要在计划中包含机器人,在世者就会真正玩在线扑克-“ PS / 2嗅探器”将记录他的行为。

至于技术,每个键盘和鼠标都有自己独特的“笔迹”。可以通过在较低级别上分析从它们发送到计算机的原始信号流来对其进行跟踪。因此,如果多个工作站同时涉及机器人驱动程序,则在准备进行生物特征识别仿真时,对于每个工作站而言,有必要:1)组织一次单独的窃听,-使用不同的键盘和不同的鼠标,2)为将要采取行动的每个工作站种植不同的人。用“ PS / 2嗅探器”固定。第二段的需要是由于每个人都单独使用鼠标和键盘。利用足够广泛的统计资料,即使用户未通过身份验证,也可以足够确定程度地确定坐在计算机旁的人。

# -

, , « »: , – , « ». , - « », , . , . , – «-» «-» – . -, , – , : , , , , . , , - , , .


因此,准备了使用合理的生物特征和技术参数进行仿真的准备。现在,您仍然需要记住哪个键盘和鼠标与哪个用户相对应-永不中断这种对应关系。忽略生物识别技术和技术计量学或它们令人难以置信的模拟可能会导致帐户封锁而无须解释。在遵守所有预防措施的前提下,无论它们看起来多么偏执,都可以依靠无懈可击的机器人。


无敌机器人的元素



图2.无敌机器人

无敌机器人是一种软硬件复合体,其中包括一整套计算机,可以解决6个根本不同的任务。加上一些硬件和软件洗剂。再加上两个有生命的人,用于观察和应对紧急情况。

1.工作站(具有专用IP或高质量PROXY),其中同时启动9个扑克室。它应该有一个带有高清模拟视频输出的视频卡和一个大监视器,因为小监视器上的许多房间看起来都很可疑。不使用一个工作站,而是一次使用约十个工作站在经济上更具成本效益。在这种情况下,该机器人可以一次在90个房间中同时启动。

2. Screener-具有视频捕获卡的功能强大的计算机,该计算机通过视频分配器连接到所有工作站。筛选器的任务是识别接收到的信号并将其转换为结构形式。此数据随后将用于1)写入数据库,2)分析和3)制定行动决策。

3.分析师-可以告诉您-强大的计算机,可以根据气象服务器的原理工作,但不会自然现象累积所有可能的扑克数据。实际上,与现场扑克相比,计算机扑克中的讲述更多。但是问题在于,如果没有辅助软件,几乎不可能跟踪它们。并且以各种可能的方式停止使用此类软件-如果在工作站上使用它,则可能会被禁止使用。

4.间谍-不会玩游戏的单独帐户,而只是从所有可用房间中依次收集所有可用信息的帐户。应该有几个间谍,因为如果同一个帐户随我们的游戏一起登录并在游戏结束时注销,则可能会将系统性的硬币扔进存钱罐怀疑中,当存钱罐存满时,我们将关闭帐户而无需给出任何理由。 “间谍”的工作原理应与“工作站”相同。似乎有足够的软件屏幕和带有键盘的鼠标模拟软件。但是,由于扑克软件可能会绑定到硬件和后续的兔子帐户,因此应该对间谍以及工作站进行硬件筛选和仿真。

5.仿真器-功率不是最基本的计算机。它连接到多个用于鼠标和键盘的分配器。从这些分离器中,将电线连接到PS / 2输入,以将鼠标和键盘连接到每个工作站和间谍。这台计算机从分析人员接收命令,并将其发送到工作站。考虑到鼠标和键盘的硬件功能,以及在其后面工作的人的行为的生理特征(生物计量学和技术)。

6.两名在世的观察员。这些不再是计算机,也就是活人。之所以需要它们,是因为扑克软件的开发人员没有睡着,他们可以随时运行一些独特的测试来识别机器人驱动程序。一个人还不够-因为他可以根据需要离开或powder鼻涕,这时会出现一些紧急情况。在某些情况下,一切都是由秒决定的。因此,重要的是始终有人靠近机器。这样,当“红色按钮亮起”时(当分析人员遇到不熟悉的行为时),一个人可以告诉他该怎么做(在聊天中写一些东西或关闭窗口)。而且在任何情况下您都不能直接进入工作站,以免破坏相应的键盘和鼠标行为模型。只能通过“命令栏”执行操作。

7.指挥所-一台功能也不重要的计算机。它仅应连接到仿真器。这正是两个实时观察者坐在的机器,如果有必要,他们可以通过它们对机器人的行为进行必要的调整。


画龙点睛


因此,这是无敌机器人的7个要素。连接它们时,应注意筛选器,分析器和仿真器通过网络交换数据。与工作站和间谍的交互完全通过视频卡和PS / 2的模拟输出进行。而且,这些计算机必须在物理上与其他所有人分开。他们之间无法通过网络进行通信。由于这种软件和硬件复杂性的复杂性,小心将与人为因素相关的错误减至最少也很重要。为此,仿真器尤其负责在工作站和间谍上启动所有必需的软件。筛选器,分析器和仿真器在打开后应立即处于警报状态-相应的软件应仅在启动时启动。


新希望



3.新希望

所描述的机器人驾驶方案,即使对此有所肤浅的了解,也不是出于胆小。但是以上给出的虽然详细,但是仍然是其表面的描述。为了实现它,无论是编程还是电子方面,都必须不具备很高的技术资格。进行编程时,开发人员将需要诸如离散数学,样条逼近,小波变换,神经网络,状态机,模糊逻辑,多线程编程,数字信号处理等领域的知识。在实现机器人的硬件时,开发人员将需要以下领域的知识,例如微处理器系统,数字和微处理器技术,与微控制器和FPGA配合使用,电气工程基础知识,低级驱动程序编程,OS体系结构和处理器体系结构。

#

– , « ». , – , , – , . , . , – SetWindowsHookEx, CreateToolHelpSnapshot32, EnumProcessModules, – . . , : , PokerStars , Visual Studio.


因此,这是一个安全的机器人驾驶方案。就像一开始所说的那样,有趣的是,即使“正义斗士”将掌握该机器人的详细注释源代码,他们也无法确定其使用的事实。而没有隐身功能的普通机器人即使没有来源也可以追踪“正义战士”。扑克软件开发人员寄予厚望的现代技术,尤其是rootkits,使他们能够深入研究操作系统,等等。奉行“坐高望远”的原则。随着Rootkit和其他防护技术的发展,“争取正义的战士”为无条件获胜赢得了新希望。但是,以上安全的机器人程序使所有这些希望变成了希望。该方案的实施是昂贵的事件,但是,由于潜在的好处是巨大的,所以该方案是非常相关的。因此,敢于冒险是有道理的。


西斯的复仇


快说不做。敢!但是,花时间在本文的其余部分上是否值得?一流的IT工程师与X场景的顶级组织有联系,而不仅仅是获得文凭的漂亮印章,这件事得到了进一步的肯定,这绝对不值得。在他的X型军火库中,已经有了足够多的神秘技巧,可以在没有他人帮助的情况下“报仇”旧机器人,这些机器人曾经在“力量的光明面”阻止了氧气。

参与X-scene的高级开发人员仅是因为他们精通C ++和汇编语言,所以不应该浪费时间,但是出于不同的原因,他们不会。所呈现的材料的详细程度对他们而言是不够的,因为在本文中,不仅故意省去了许多琐碎的编程任务(高级开发人员处理了它们的二至三),而且还忽略了一些基本的工程概念。至于甚至不讲会话C ++和汇编程序的脚本脚本,毫无疑问本文对他们有什么好处。

但是,一个普通的工程师已经离开了十几岁的年龄(已经发展了一点工程学的才能),但是还没有进入成人工程时代,他会在这里找到有趣的想法,这些想法将帮助他实现无敌的机器人。只要他已经能够使用可用资源为看似复杂的任务找到简单的解决方案。而这恰恰是X场景在其亮面和暗面所珍视的神秘力量。拥有它的能力是工程师与开发人员的区别所在。因此,让我们为“争取正义的战士”设置热度,以显示力量的阴暗面所具有的能力。


帝国反击



图4.帝国反击

好消息:最艰难的部分已经过去。战略行动计划也是对问题的非正式陈述,已经准备就绪。现在仅需稍微说明和详细说明-以战术行动来描述。在细化阶段,最重要的是冷静地“从一般到特殊”,等等。形成未来项目的框架。同时,无需深入介绍特定功能单元的实现细节,只需对其进行修复即可。当框架已经完全准备就绪时,我们将在稍后处理它们的细节。

#

, – . , – . 99- . 10- , , , , : « , ». , IDE .


为了更加清晰,我们将使用Fort语言的语法(不要与Fortran混淆)。这是一门独特的语言,自然会促进工程学的合理发展。这种“堡垒借用”方法的优点在于,该项目的所有概念缺陷都在岸上发现,而不是在项目实施的中间,这从一开始就促进了高质量的发展。无需后续重构。因此,框架“从一般到特殊”:
框架“从一般到特定”
: -
  - -
  - -
  - --
  -
;

: --
  - !!!!!!
  - !!!!!!
  - !!!!!!
  - 
  - -
;

: -
   --
  - -
  --
;

: -
  - --
  - -
  --
;

: !!!!!!
  --- -
  -- -
  --
;

: -
  ---
  -----
  --
;

: --
  -- -
  - -
  - -
  - -
  - -
  ---
  - 
;

: !!!!!!
  -- --
  -- -
  -- -
  -- --
  -
  --
  ---
  --
;

: !!!!!!
  ---
  --
  -
  -
  --
;

: !!!!!!
  --
  ---
;

: !!!-!!!
  --
  ---
;

: --
  --
  ---
  ---
;


在草拟项目的总体框架时使用Fort语言的语法的另一个优点是能够立即编译书面文本。因为这段文字本身就是一个程序。原则上,如果使用Fort语言实现所有缺少的功能节点,并将它们添加到上述框架中,则会得到我们正在使用的相同的无敌bot。结果将非常简洁。但是,下面的代码段将使用更传统的语言-C ++和汇编程序编写。在这种情况下,“苦苦挣扎”的介绍仅出于项目总体框架的说明目的而有趣。因此,经历了“从一般到特定的平静”,现在我们将更详细地考虑该机器人的某些功能节点的实现细节。


隐形摄影


在这里,我们有三个基本任务:1)找到一个可接受的硬件解决方案,2)开发将从照片中读取的数据结构,3)实现识别功能。摄影阶段的结果应该是一个完整且冗余的数据结构,基于此逻辑,自动机将能够唯一地识别游戏的当前状态-尽可能减少连续射击的次数。

1.对于硬件解决方案,理想的选择(从经济角度和易于后续编程的角度出发)是,所有视频信号都汇聚在一个视频分配器上,可以从计算机切换该视频分配器。在这种情况下,筛选任务可以由一台带有一张视频捕获卡的计算机执行。在这种情况下,对于每个间谍和工作站,都需要创建一个文件夹-相应的屏幕快照将保存在该文件夹中。如果无法实施此选项(由于无法使用适当的设备和/或由于技术资格不足而无法使现有设备适应您的需求),则可以使用连接到本地网络的多台计算机,每张中都会有几张视频采集卡-每个“间谍”和“工作站”一张。在这种情况下,您将需要将所有扫描的位图添加到共享的网络文件夹中,或者在客户端-服务器应用程序套接字上写入,这会将所有位图收集到一个位置。要下载位图,以便随后识别它们,可以使用以下代码:
下载位图
void LoadRamaFromFile(LPCSTR lpcszFileName, HDC *lphdcRama)
{
  HANDLE hBitmap = LoadImage(0, lpcszFileName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
  *lphdcRama = CreateCompatibleDC(NULL); //    
  SelectObject(*lphdcRama, hBitmap);   //    
  DeleteObject(hBitmap);         //  
}


2.关于数据结构,可以如下:
资料结构
// 
typedef enum ROUND
{
  RES_WAITINGBLINDS, //   ( )
  RES_FLOPHIDDEN,  //  ,      ()
  PREFLOP,      // 
  FLOP,       // 
  TURN,       // 
  RIVER,       // 
  UNKNOWN,      // 
  ZSTATE
} *LPROUND;

//                         //
typedef enum ACTION
{
  AC_POSTSB,   //   
  AC_POSTBB,   //   
  AC_POSTSBANDBB,
  AC_CHECK,    //    
  AC_BET,     //   
  AC_RAISE,    //   
  AC_CALL,    //   
  AC_FOLD,    //  
  AC_ALLIN,    //  
  AC_SITOUT,   //            
  AC_MUCK,    //   
  AC_SHOWCARDS,  //   
  AC_NONE,    //  ,     (  )
  AC_TNB,     //    ( )
  AC_ZSTATE
} *LPACTION;

//    
typedef struct tagSITA_UVACHA
{
  char  szNickname[STR_SIZE_NICKNAME];  // 
  char  szStack[STR_SIZE_STACK];     //  
  char  szHoleCards[STR_SIZE_HOLECARDS]; //     ( )
  ACTION  action;
} SITA_UVACHA, *LPSITA_UVACHA;

//    
typedef struct tagRAMA_UVACHA
{
  SITA_UVACHA  sita[MAX_COUNT_SITA];   //    
  DWORD  dwCountSita;           //    
  DWORD  dwBUPos;             //  
  char  szPOT[STR_SIZE_POT];       //  
  char  szBoardCards[STR_SIZE_HOLECARDS]; // ,    
  ROUND  round;              //  
} RAMA_UVACHA, *LPRAMA_UVACHA;

////////////////////////////

typedef struct tagKRISHNA_UVACHA
{
  RAMA_UVACHA  rama;
  DWORD    dwFirstSaid;    //      
  DWORD    dwLastSaid;     //      
  ACTION    last_action;    //     
} KRISHNA_UVACHA, *LPKRISHNA_UVACHA;


3.识别功能的一种可能选择是为此配置一个神经网络。该主题值得单独讨论,因此在本文中我们将不讨论神经网络方法。一个简单的选项是附加到相应的位图。相应的数据结构如下所示:

识别数据结构
typedef struct tagSURJA_CRAPH_DATA
{
  BYTE  PATTERN_LETTER[COUNT_LETTERS][SIZE_PATTERN_LETTER];       //  
  BYTE  LETTER_CODE[COUNT_LETTERS];                   //  
  BYTE  LETTER_SIZE[COUNT_LETTERS];                   //  

  BYTE  PATTERN_INSCRIPT[COUNT_INSCRIPTIONS][SIZE_PATTERN_INSCRIPTION]; //   
  char  INSCRIPTION_TEXT[COUNT_INSCRIPTIONS][SIZE_INSCRIPTION_TEXT];  //  
  BYTE  PATTERN_CARD[COUNT_CARDS][SIZE_PATTERN_CARD];          //  
  char  CARD_TEXT[COUNT_CARDS][SIZE_CARD_TEXT];             //  
  BYTE  PATTERN_HOLEHIDDEN[COUNT_HOLEHIDDEN][SIZE_PATTERN_HOLEHIDDEN]; //    
  char  HOLEHIDDEN_TEXT[COUNT_HOLEHIDDEN][SIZE_HOLEHIDDEN_TEXT];    //     
} SURJA_CRAPH_DATA, *LPSURJA_GRAPH_DATA;


填写它的最简单方法是手动。只需截屏,在Photoshop中将其打开,激活吸管工具并重写我们感兴趣的区域中的数字即可。即使大步前进,这一决定也不能简明扼要。此外,它还有很大的局限性-即使扑克软件的日程安排中最微小的变化也将导致机器人停止工作。但是,如果技术资格不允许您提出更有价值的建议,那么“手动识别”的准备工作可能是这样的(这里仅是近2000行代码中的一小部分):
手动识别
BOOL LoadGraphData()
{
//=====================     ================
  memset(m_BramaGraphData.ptRamaCoords,  0,  sizeof(POINT)*COUNT_VARRIOUS_RAMA*COUNT_RAMA_VALUES);
  memset(m_BramaGraphData.ptSitaCoords,  0,  sizeof(POINT)*COUNT_VARRIOUS_RAMA*MAX_COUNT_SITA*COUNT_SITA_VALUES);
  memset(m_BramaGraphData.ptRecognizeSize,0,  sizeof(POINT)*COUNT_VAL_SIZE);
//=====================  9-  ===============
  m_BramaGraphData.ptRamaCoords[INDEX_RAMA_AT_9_SITA][INDEX_VAL_RAMA_POT].x  = 210;
  m_BramaGraphData.ptRamaCoords[INDEX_RAMA_AT_9_SITA][INDEX_VAL_RAMA_POT].y  = 34;
  m_BramaGraphData.ptSitaCoords[INDEX_RAMA_AT_9_SITA][0][INDEX_VAL_SITA_NICKNAME].x    = 340;
  m_BramaGraphData.ptSitaCoords[INDEX_RAMA_AT_9_SITA][0][INDEX_VAL_SITA_NICKNAME].y    = 44;
  m_BramaGraphData.ptSitaCoords[INDEX_RAMA_AT_9_SITA][1][INDEX_VAL_SITA_NICKNAME].x    = 423;
  m_BramaGraphData.ptSitaCoords[INDEX_RAMA_AT_9_SITA][1][INDEX_VAL_SITA_NICKNAME].y    = 77;
  m_BramaGraphData.ptSitaCoords[INDEX_RAMA_AT_9_SITA][2][INDEX_VAL_SITA_INSCRIPTION].x  = 438;
  m_BramaGraphData.ptSitaCoords[INDEX_RAMA_AT_9_SITA][2][INDEX_VAL_SITA_INSCRIPTION].y  = 165;
//=====================  10-  ==============
//=====================    ==================
  m_BramaGraphData.PATTERN_LETTER[PAT_0][0] = b01111000;
  m_BramaGraphData.PATTERN_LETTER[PAT_0][1] = b10000100;
  m_BramaGraphData.PATTERN_LETTER[PAT_8][2] = b10100100;
  m_BramaGraphData.PATTERN_LETTER[PAT_8][3] = b01011000;
  m_BramaGraphData.LETTER_CODE[PAT_0] = '0';
  m_BramaGraphData.LETTER_CODE[PAT_1] = '1';
  m_BramaGraphData.LETTER_CODE[PAT_2] = '2';
//=====================     ================
  m_BramaGraphData.PATTERN_INSCRIPT[PAT_INSCRIPTION_SEATOPEN][0]  = 55;
  m_BramaGraphData.PATTERN_INSCRIPT[PAT_INSCRIPTION_SEATOPEN][1]  = 56;
  m_BramaGraphData.PATTERN_INSCRIPT[PAT_INSCRIPTION_SEATOPEN][2]  = 124;
  m_BramaGraphData.PATTERN_INSCRIPT[PAT_INSCRIPTION_SEATOPEN][3]  = 215;
//=====================     ==================
  lstrcpy(m_BramaGraphData.INSCRIPTION_TEXT[PAT_INSCRIPTION_SEATOPEN],  "SEATOPEN");
  lstrcpy(m_BramaGraphData.INSCRIPTION_TEXT[PAT_INSCRIPTION_EMPTYSEAT],  "EMPTYSEAT");
  lstrcpy(m_BramaGraphData.INSCRIPTION_TEXT[PAT_INSCRIPTION_CALL],    "CALL");
  lstrcpy(m_BramaGraphData.INSCRIPTION_TEXT[PAT_INSCRIPTION_BET],      "BET");
//=====================    =======================
  m_BramaGraphData.PATTERN_CARD[PAT_CARD_EMPTY][0] = 30;
  m_BramaGraphData.PATTERN_CARD[PAT_CARD_EMPTY][1] = 30;
  m_BramaGraphData.PATTERN_CARD[PAT_CARD_EMPTY][2] = 30;
  m_BramaGraphData.PATTERN_CARD[PAT_CARD_HIDE][6] = 188;
  m_BramaGraphData.PATTERN_CARD[PAT_CARD_HIDE][7] = 151;
//=====================    ==================
  lstrcpy(m_BramaGraphData.CARD_TEXT[PAT_CARD_EMPTY],  "");
  lstrcpy(m_BramaGraphData.CARD_TEXT[PAT_CARD_HIDE],  "hid");
  lstrcpy(m_BramaGraphData.CARD_TEXT[PAT_CARD_2C],  "2C");
  lstrcpy(m_BramaGraphData.CARD_TEXT[PAT_CARD_4H],  "4H");
//=====================     =================
//=====================      ===
  lstrcpy(m_BramaGraphData.HOLEHIDDEN_TEXT[PAT_HOLEHIDDEN_EMPTY],  "");
  lstrcpy(m_BramaGraphData.HOLEHIDDEN_TEXT[PAT_HOLEHIDDEN_ONE],  "one-hidden");
  lstrcpy(m_BramaGraphData.HOLEHIDDEN_TEXT[PAT_HOLEHIDDEN_TWO],  "hidden");
//=====================   ===========================
  m_BramaGraphData.PATTERN_MISC[PAT_MISC_BUTTON][0] = 216;
  m_BramaGraphData.PATTERN_MISC[PAT_MISC_BUTTON][1] = 72;
  m_BramaGraphData.PATTERN_MISC[PAT_MISC_BUTTON][2] = 8;
  m_BramaGraphData.PATTERN_MISC[PAT_MISC_BUTTON][3] = 151;
  m_BramaGraphData.PATTERN_MISC[PAT_MISC_BUTTON][4] = 221;
  m_BramaGraphData.PATTERN_MISC[PAT_MISC_BUTTON][5] = 194;
  m_BramaGraphData.PATTERN_MISC[PAT_MISC_BUTTON][6] = 231;

  return TRUE;
}


这是使用此类位图进行识别的代码示例:
识别码示例
typedef struct tagSURJA_CRAPH_DATA
{
  BYTE  PATTERN_LETTER[COUNT_LETTERS][SIZE_PATTERN_LETTER];       //  
  BYTE  LETTER_CODE[COUNT_LETTERS];                   //  
  BYTE  LETTER_SIZE[COUNT_LETTERS];                   //  

  BYTE  PATTERN_INSCRIPT[COUNT_INSCRIPTIONS][SIZE_PATTERN_INSCRIPTION]; //   
  char  INSCRIPTION_TEXT[COUNT_INSCRIPTIONS][SIZE_INSCRIPTION_TEXT];  //  
  BYTE  PATTERN_CARD[COUNT_CARDS][SIZE_PATTERN_CARD];          //  
  char  CARD_TEXT[COUNT_CARDS][SIZE_CARD_TEXT];             //  
  BYTE  PATTERN_HOLEHIDDEN[COUNT_HOLEHIDDEN][SIZE_PATTERN_HOLEHIDDEN]; //    
  char  HOLEHIDDEN_TEXT[COUNT_HOLEHIDDEN][SIZE_HOLEHIDDEN_TEXT];    //     
} SURJA_CRAPH_DATA, *LPSURJA_GRAPH_DATA;



一种或另一种方式-即使我们至少使用双手,至少使用第三手使用神经网络-在识别中,应考虑四个根本不同的领域:1)静态(根据理论,该领域什么都不会发生);包括 在扑克软件外部(例如,某种行为发生在扑克客户端外部,可能是Windows系统引起的,但实际上-检查是否具有可操作性);2)动态信息丰富(堆栈大小,当前玩家,桌上的纸牌等);3)动态信息不足(很少出现消息和聊天的地方);4)动态非信息性的(例如,发生纸牌分发动画的区域)。


看不见的分析


就扑克而言,机器人的分析部分是一种与数据库进行交互的机制-因为几乎所有与在线扑克相关的“诉说”都依赖于对游戏对手行为模型的分析。这里的理想选择是您自己的家用Oracle,它在密集处理包含数十万条记录的大型数据库中必不可少。即,如果您计划长期认真从事植物种植,将会有很多这样的人。如果您只需要一个机器人来沉迷,那么您就不能在Oracle上花钱。MS Access DBMS也非常适合-您可以从程序中轻松地与它联系,例如通过ODBC。可能是这样的:
与MS Access的交互
BOOL InitODBC()
{
  SQLRETURN    ret;
  SQLSMALLINT    Length;

  ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_HANDLE_NULL, &hEnv);
  if (SQL_SUCCESS != ret && SQL_SUCCESS_WITH_INFO != ret)
    return FALSE;

  ret = SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, NULL);
  if (SQL_SUCCESS != ret && SQL_SUCCESS_WITH_INFO != ret)
    return FALSE;

  ret = SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hConn);
  if (SQL_SUCCESS != ret && SQL_SUCCESS_WITH_INFO != ret)
    return FALSE;

  lstrcpyn(szConnect, STR_CONNECT, MAXSIZE_CONNECTSTR);
  ret = SQLDriverConnect(hConn, NULL, (SQLTCHAR *) szConnect, lstrlen(szConnect), (SQLTCHAR *) szConnect, MAXSIZE_CONNECTSTR, &Length, SQL_DRIVER_COMPLETE);
  if (SQL_SUCCESS != ret && SQL_SUCCESS_WITH_INFO != ret)
    return FALSE;

  return TRUE;
}

void DoneODBC()
{
  SQLDisconnect(hConn);
  SQLFreeHandle(SQL_HANDLE_DBC, hConn);
  SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
}



BOOL InsertInDB(LPCSTR lpcszText, LPCSTR lpcszTitle, DWORD dwLocale)
{
  SQLHANDLE  hStmt;
  SQLRETURN  ret;
  char    szTitle[SIZE_TITLE];
  char    szQuery[MAXSIZE_QUERYSTR];


  //          //
  ret = SQLAllocHandle(SQL_HANDLE_STMT, hConn, &hStmt);
  if (SQL_SUCCESS != ret && SQL_SUCCESS_WITH_INFO != ret)
    return FALSE;

  wsprintf(szQuery, "INSERT INTO index (text, title, locale) VALUES ('%s', '%s', %d)", lpcszText, szTitle, dwLocale);

  ret = SQLExecDirect(hStmt, (SQLTCHAR *) szQuery, lstrlen(szQuery));

  if (SQL_SUCCESS != ret && SQL_SUCCESS_WITH_INFO != ret)
    return FALSE;
  SQLFreeHandle(SQL_HANDLE_STMT, hStmt);

  return TRUE;
}


例如,如果与扑克无关,而与国际象棋有关,那么可以将与第三方国际象棋引擎的交换作为分析的一部分。例如,以下是与Fritz进行交互的方法:
与分析引擎的交互
BOOL SendData2Fritz(char szFritz[100])
//    //
{
  if (!FritzStr2Clipboard(szFritz))
    return FALSE;
  SendMessage(m_hwndFritz, WM_COMMAND, MAKELONG(IDM_PASTE_POSITION, 0), 0); 
  Sleep(2);
  SendMessage(m_hwndFritz, WM_COMMAND, MAKELONG(IDM_MOVE_NOW, 0), 0); 

  return TRUE;
}


BOOL ReceiveDataFromFritz(int busy[8][8], LPBOOL lpbEndGame)
//     //
{
  char szFritz[100];

  *lpbEndGame = FALSE;

  SendMessage(m_hwndFritz, WM_COMMAND, MAKELONG(IDM_COPY_POSITION, 0), 0);
  if (!Clipboard2FritzStr(szFritz))
    return FALSE;
  return TRUE;
}

BOOL FritzStr2Clipboard(LPCSTR lpcszFritz)
//         //
{
  HGLOBAL hGlobalMemory;    //   
  LPVOID pGlobalMemory;     //   

  hGlobalMemory = GlobalAlloc(GHND, lstrlen(lpcszFritz)+1);
  if (hGlobalMemory == NULL)
    return FALSE;
  pGlobalMemory = GlobalLock(hGlobalMemory);
  lstrcpy((LPSTR) pGlobalMemory, lpcszFritz);
  GlobalUnlock(hGlobalMemory);

  if (!OpenClipboard(NULL))
    return FALSE;
  if (!EmptyClipboard())
    return FALSE;
  SetClipboardData(CF_TEXT, hGlobalMemory);
  CloseClipboard();

  return TRUE;
}

BOOL Clipboard2FritzStr(LPSTR lpszFritz)
//        //
{
  HANDLE hClipMemory;    //   
  LPVOID pClipMemory;    //     

  if (IsClipboardFormatAvailable(CF_TEXT))
  {
    if (!OpenClipboard(NULL))
      return FALSE;
    hClipMemory = GetClipboardData(CF_TEXT);
    if (hClipMemory == NULL)
      return FALSE;
    pClipMemory = GlobalLock(hClipMemory);
    lstrcpyn(lpszFritz, (LPSTR) pClipMemory, 100);
    GlobalUnlock(hClipMemory);
    CloseClipboard();
  }
  return TRUE;
}



编程分析时可以使用的另一种工具是所谓的。“有限状态机”。以下是如何在在线多人游戏中使用它们的方法(这是机器人“力量守护者”的片段):
状态机示例
typedef enum AUTOMATA_BATLE
{
  //  
  AS_BATLE_BEGIN,
  AS_BATLE_END,
  AS_BATLE_ERROR,

  AS_BATLE_TEST2MOB,     // ,    
  AS_BATLE_WAIT2NICK,    //    
  AS_BATLE_WAIT2MOB,     //   
  AS_BATLE_PROCESS,     //   ,    
  AS_BATLE_CLICK2OK_1,    //  OK
  AS_BATLE_WAITCHANGE,    //   
  AS_BATLE_CLICK2MONEY,   //    ""
  AS_BATLE_TEST2DROP,    //     (  "")
  AS_BATLE_CLICK2DROP,    //    ""
  AS_BATLE_CLICK2CLOSE,   //    ""
  AS_BATLE_WAIT2DROP,    //     
  AS_BATLE_CLICK2TAKEALL,  //  " "
  AS_BATLE_WAIT2CONFIRM,   //  
  AS_BATLE_CLICK2OK_2,    //  
  AS_BATLE_WAIT2BACK,    //   
  AS_BATLE_CLICK2BACK,    //  ""
  AS_BATLE_WAIT2STARTWINDOW, //    
  AS_BATLE_WAIT2NEXT     //  30 
};



AUTOMATA_BATLE CAutomataBatle::GoStep(HDC hdc)
{
  char szMessage[255];

  switch (m_automata_batle)
  {
  //        //
  case AS_BATLE_BEGIN:
    m_automata_batle = AS_BATLE_WAIT2STARTWINDOW;
    SetWindowText(m_hwndControl, "  ");
    break;
  case AS_BATLE_WAIT2STARTWINDOW:
    if (IsStartPresent(hdc))
    {
      SetWindowText(m_hwndControl, " ");
      m_automata_batle = AS_BATLE_TEST2MOB;
    }
    break;
  case AS_BATLE_TEST2MOB:
    if (IsMechPresent(hdc))
      if (!ClickMenuItem(INDEX_GOBATLE))
        return AS_BATLE_ERROR;
    if (IsCellEmpty(hdc))
    {
      SetWindowText(m_hwndControl, "  ");
      m_automata_batle = AS_BATLE_END;
    }
    else
    {
      hwndBatle = NULL;
      EnumChildWindows(m_hwndMain, EnumWindowsProcBatle, NULL);

      if (NULL != hwndBatle)
      {
        SetWindowText(m_hwndControl, "  ");
        m_automata_batle = AS_BATLE_WAIT2NICK;
      }
    }
    break;
  case AS_BATLE_WAIT2NICK:
    if (IsNickPresent(hdc))
    {
      SetWindowText(m_hwndControl, "  ");
      m_automata_batle = AS_BATLE_WAIT2MOB;
    }
    break;
  case AS_BATLE_WAIT2MOB:
    if (!IsNickOpEmpty(hdc))
    {
      SetWindowText(m_hwndControl, " ");
      m_automata_batle = AS_BATLE_PROCESS;
    }
    break;
  case AS_BATLE_PROCESS:
    if (!IsBatleEnd(hdc))
    {
      if (!ClickMenuItem2Window(hwndBatle, INDEX_STRIKE))
        return AS_BATLE_ERROR;
    }
    else
    {
      SetWindowText(m_hwndControl, "  ");
      m_automata_batle = AS_BATLE_CLICK2OK_1;
    }
    break;
  case AS_BATLE_CLICK2OK_1:
    if (!ClickMenuItem2Window(hwndBatle, INDEX_OK2BATLEEND))
      return AS_BATLE_ERROR;
    SetWindowText(m_hwndControl, "  ");
    m_automata_batle = AS_BATLE_WAITCHANGE;
    break;
  case AS_BATLE_WAITCHANGE:
    if (IsChangePresent(hdc))
    {
      SetWindowText(m_hwndControl, " ");
      m_automata_batle = AS_BATLE_CLICK2MONEY;
    }
    break;
  case AS_BATLE_CLICK2MONEY:
    if (!ClickMenuItem(INDEX_MONEY))
      return AS_BATLE_ERROR;
      SetWindowText(m_hwndControl, " ");
    m_automata_batle = AS_BATLE_TEST2DROP;
    break;
  case AS_BATLE_TEST2DROP:
    if (IsButtonDropPresent(hdc))
    {
      SetWindowText(m_hwndControl, "  \"\"");
      m_automata_batle = AS_BATLE_CLICK2DROP;
    }
    else if (IsButtonClosePresent(hdc))
    {
      SetWindowText(m_hwndControl, " ,   \"\"");
      m_automata_batle = AS_BATLE_CLICK2CLOSE;
    }
    break;
  case AS_BATLE_CLICK2DROP:
    if (IsButtonDropPresent(hdc))
    {
      if (!ClickMenuItem(INDEX_DROP))
        return AS_BATLE_ERROR;
    }
    else
    if (IsDropPresent(hdc))
    {
      SetWindowText(m_hwndControl, "  ");
      m_automata_batle = AS_BATLE_WAIT2DROP;
    }
    else if (IsStartPresent(hdc))
      m_automata_batle = AS_BATLE_WAIT2STARTWINDOW;
    break;
  case AS_BATLE_CLICK2CLOSE:
    if (!ClickMenuItem(INDEX_CLOSE))
      return AS_BATLE_ERROR;
    if (IsStartPresent(hdc))
    {
      SetWindowText(m_hwndControl, "  ");
      m_dwWait = 0;
      m_automata_batle = AS_BATLE_WAIT2NEXT;
    }
    break;
  case AS_BATLE_WAIT2DROP:
    if (IsDropPresent(hdc))
    {
      SetWindowText(m_hwndControl, "  ");
      m_automata_batle = AS_BATLE_CLICK2TAKEALL;
    }
    break;
  case AS_BATLE_CLICK2TAKEALL:
    if (!ClickMenuItem(INDEX_TAKEALL))
      return AS_BATLE_ERROR;
    SetWindowText(m_hwndControl, "  ");
    m_automata_batle = AS_BATLE_WAIT2CONFIRM;
    break;
  case AS_BATLE_WAIT2CONFIRM:
    if (IsConfirmPresent(hdc))
    {
      SetWindowText(m_hwndControl, "");
      m_automata_batle = AS_BATLE_CLICK2OK_2;
    }
    break;
  case AS_BATLE_CLICK2OK_2:
    if (!ClickMenuItem(INDEX_OK2TAKEALL))
      return AS_BATLE_ERROR;
    if (!IsConfirmPresent(hdc))
    {
      SetWindowText(m_hwndControl, "  \"\"");
      m_automata_batle = AS_BATLE_WAIT2BACK;
    }
    break;
  case AS_BATLE_WAIT2BACK:
    if (IsBackPresent(hdc))
    {
      SetWindowText(m_hwndControl, "  \"\"");
      m_automata_batle = AS_BATLE_CLICK2BACK;
    }
    break;
  case AS_BATLE_CLICK2BACK:
    if (!ClickMenuItem(INDEX_BACK))
      return AS_BATLE_ERROR;
    if (IsStartPresent(hdc))
    {
      SetWindowText(m_hwndControl, "  ");
      m_dwWait = 0;
      m_automata_batle = AS_BATLE_WAIT2NEXT;
    }
    break;
  case AS_BATLE_WAIT2NEXT:
    if (m_dwWait >= WAIT_2NEXTBATTLE)
    {
      SetWindowText(m_hwndControl, "  ");
      m_automata_batle = AS_BATLE_WAIT2STARTWINDOW;
    }
    else
    {
      wsprintf(szMessage, "(%d  %d) ...", m_dwWait/2, WAIT_2NEXTBATTLE/2);
      SetWindowText(m_hwndControl, szMessage);
      m_dwWait++;
    }
    break;
  case AS_BATLE_END:
    m_automata_batle = AS_BATLE_BEGIN;
    break;
  }




隐形仿真


有两个阶段-准备阶段和操作阶段。两者都暗示着不仅编写适当的程序,还暗示着使用Iron。在这里,您不能不沉浸在微控制器中。幸运的是,无需重新发明轮子-可以在Internet上轻松找到现成的软件和硬件解决方案。包括微控制器程序的源代码,可以轻松地适应您的需求。

至于准备阶段(使用鼠标和键盘记录工作时间),如果需要,您可以在没有硬件小工具的情况下进行。您可以编写一个驱动程序筛选器,以捕获对IOCTL_INTERNAL_I8042_HOOK_KEYBOARD的调用。作为基础,您可以使用ctrl2cap程序,该程序的源代码在DDK中可用。但是,此解决方案不是通用的-最好使用硬件嗅探器。

至于操作阶段,没有硬件配件绝对是没有办法的。一种可能的解决方案是,一方面通过RS-232与微控制器设备(在组合中也将是分离器)通信,另一方面通过PS / 2束进行通信。要使用RS-232,有两种选择-您可以使用常规Windows工具并通过带有RS-232端口的CreateFile / ReadFile / WriteFile进行通信,也可以直接通过输入/输出端口进行通信。在第二种情况下,您将需要编写一个简单的驱动程序来解锁对I / O端口的访问,因为默认情况下,Windows中对它们的访问是关闭的。远离罪恶。如果您仍然设法访问输入/输出端口,那么以下是通过RS-232交换数据的代码:
RS-232代码
#define dataport  0x02F8   //   COM2
#define irport   0x02F9   //       
#define manager   0x02FB   //  
#define statline  0x02FD   //  
#define upr1    0x00BB      
#define upr2    0x003B
#define freq    0x000C   //    (9600 )

DWORD OpenPort()
/*  COM- */
{
  __asm
  {
    mov  eax, upr1    //   0  1    
    mov  edx, manager
    out  dx, al

    mov  eax, freq    //    
    mov  edx, dataport
    out  dx, al     //       

    mov  al, ah
    mov  edx, irport
    out  dx, al     //       

    mov  eax, upr2
    mov  edx, manager
    out  dx, al

    mov  al, 0      //  
    mov  edx, irport
    out  dx, al
  }
}

DWORD WriteByte(BYTE data)
/*    COM- */
{
  __asm
  {
    mov  al, data    //    
    mov  edx, dataport
    out  dx, al     //     
waitout:
    mov  edx, statline
    in   al, dx     //   
    mov  ah, al
    and  al, 0x40    // ,    
    jz   waitout     //   – 
  }
} // void WriteByte

BOOL ReadByte(BYTE data)
/*    COM- */
{
  int cntWait = 0;

  __asm
  {
    mov  edx, statline
    xor  ecx, ecx
waitread:
    add  cntWait, 1
    cmp  cntWait, 0xFFFF
    jz   error
    in   al, dx     //   
    mov  bl, al     // ,   
    and  eax, 1
    jz   waitread    //    –  
    mov  edx, dataport  //   –   
    xor  eax, eax
    in   al, dx     //     
    mov  data, al
error:
  }
  if (cntWait != 0xFFFF)
    return TRUE;
  else
    return FALSE;
} // BYTE ReadByte


因此,用鼠标截取和仿真键盘信号的机制是可以理解的,仅需弄清楚如何从生物特征学角度从嗅探器接收到的连续的键盘和鼠标数据流中,创建可信的行为模型。此外,此阶段最有趣的任务之一是如何将鼠标从起点移动到另一只经过专门设置的鼠标。

一种可能的解决方案包括两个要素:1)配置神经网络,2)调整运动并教导神经网络与“筛选器”协同工作。通过这种方法,可以在最少的人工干预下训练神经网络。神经网络训练算法可以如下:
  1. 我们选择鼠标应移动到的随机坐标。
  2. 我们将它们与当前坐标一起馈入神经网络。
  3. 我们看看神经网络有多大错误。
  4. 我们调整神经网络。
  5. 重复1-4,直到取得胜利。给定从硬件嗅探器接收到的数据,神经网络不仅可以准确地移动鼠标光标,而且还可以进行生物识别。



接下来是什么


这种神经网络方案的实现以及上面提到的神经网络屏幕识别方案,是超出本文范围的单独讨论的主题。这两个超出了本文范围的任务是邀请“中级工程师”独立解决的非常“基本的工程概念”。对于“琐碎的编程问题”,大多数问题的实现也未包含在本文中。本文列出的那10条清单只是种子,旨在帮助提高工程学的胃口。

另一个重点。在开发自己的机器人时使用本文,您应该了解:当提供几种替代选项(理想的和一些折衷的)来实现特定的功能节点时,这种选择是修辞性的。可以理解,将使用理想的选择。如果您做出让步,即使只有一次,则该机器人将很容易受到“力量的光明面”的反击。


绝地归来



图5.绝地武士的回归

因此,上述无懈可击的机器人驾驶计划面对“争取正义的战士”,声称是最终的和不可撤销的胜利。但是,如果“权力的光明面”的绝地没有机会反对僵尸妇女,那将很无聊。而且,如果这不可能,那么作者将坚持“势力的光明面”,不会发表这一计划。另外,我想相信很大一部分读者也坚持“力量的光明面”,他们的兴趣不是滥用该计划,而是如何抵抗它。本文没有提供详细的源代码,并且省略了一些基本的工程概念-正是为了避免滥用。

关于如何抵抗所描述的植物生产方案的几句话。她几乎是无敌的。但是,发现它的理论可能性仍然存在。此功能与跟踪可能滑落的非典型鼠标行为相关联,即使在进行高质量仿真时也是如此。事实是,老鼠的尾巴上有很多“生物特征”,以扑克语言表达。她离这个人最近。由情感背景的变化引起的最轻微的肌肉运动-所有这些都会影响我们将鼠标握在手中的方式。

在这方面,本文的作者进行了原始的仿生研究,其工作标题为“智人的集成体系结构”,其中包括对人的生理和心理的描述-适用于IT操作的模型。得益于这项研究的积极成果,我感到有道德权利公开“无懈可击”的机器人驾驶计划,因为“权力的光明面”有机会获胜。通过这项研究,除其他外,我们可以通过脊髓的界面了解我们对鼠标的使用与我们的心理活动之间的关系。从某种程度上说,这可以比喻地读懂将手放在鼠标上的人的想法。正是在这种“思想解读”中,看到了“争取正义的战士”反击的可能载体。

T.O. 部队黑暗与光明之间的战斗仍在继续。一些试图假装是人,而另一些则监视鼠标的非典型行为。创建一个虚拟鼠标模拟器就像创建一个Jedi光剑一样。发现虚拟仿真的事实与用另一把光剑击退这把剑的打击一样。那么谁会更坚强呢?


权力的觉醒



6.唤醒权力

无论采用哪种方式,无论是机器人还是反对机器人,我们都必须提高拥有光剑的技术。从自身的能力发展机械的意义上讲。它的主要组成部分是专注于任务的能力,可以“在此时此刻”。本质上,这种瑜伽完美是三摩地。梵语中的Samadhi意味着-全神贯注于一个主题。全神贯注!如果用透镜拾取太阳光束,则指向其的表面将被点燃。同样,在工程中。我们拥有的所有能量,我们必须集中精力作为一个镜头,并将其用于工程中。但是,请勿像使用镜片并开始燃烧蚂蚁的孩子那样使用其能量。应该善用创造力。

# #

-, – , – IDE , , : «#define QUESTION bb || !bb». « – », . – 5000 , – , : , – « ». , «-», «-». , , , , . . , « », 1880- , , . – - ,可以在Srimad-Bhagavatam的页面上详细阅读,该内容对应于OM声音,其方式与现代高级编程语言对应于汇编器的方式相同(有关更多详细信息,请参见“代码#代码”)。


专注的基础是精神的核心,您可以将这种高科技的小工具用作“书”,以增强精神。当然是一本好精神书。一本“书”是技术上的硬道理:1)轻轻一按即可打开书本; 2)配备无闪烁的屏幕; 3)电池电量充足。对于本文的作者来说,这就是Srimad-Bhagavatam(在苏联时期是严格禁止的)。您可以在“《博伽瓦谭的使命》”一文中阅读有关内容

此外,为了专心致志,无论多么平凡,都需要健康的身体和精神调。有很多方法可以调音。例如,对于本文的作者来说,这是素食(Vedic美食),运动服(滑雪的kmc)和禅修(冥想Hare Krsna,Hare Krsna,Krsna Krsna,Hare Hare / Hare Rama,Hare Rama,Rama Rama,野兔(Hare Hare)。您可以在文章“野兔克里希纳咒语”中阅读有关此咒语的信息您可以使用我的经验,也可以使用自己的经验。愿力量与我们同在。

Source: https://habr.com/ru/post/zh-CN389433/


All Articles